Starting Rust notes

I started learning Rust (again). Right now, I'm translating a bunch of my astronomy and astrodynamics code (in a mix of C++, Go, and Forth). I do want to start working on a delay-tolerant networking package too, it's been a long-standing interest.

Library organization

First, the tree will look something like this:

astro-rs/
├── Cargo.toml
├── LICENSE
├── README.md
├── src/
│   ├── lib.rs
│   ├── dms.rs
│   ├── dts.rs
│   └── fpm.rs

Then, lib.rs will look something like

//! astro is a collection of software for doing astronomy
//! and astrodynamics.

pub mod dts;
pub mod dms;
pub mod fpm;
  • The pub mod lines tell Rust to include these modules and make them accessible to users of your library.
  • Library-level code can be added here if needed (e.g., common traits or enums).

Then, dms.rs might look like

//! date-time systems.
use std::fmt;

#[derive (Debug, Clone, Copy)]
pub struct Time {
    pub hour: i32,
    pub minute: i32,
    pub second: f64,
}

pub const MIDNIGHT: Time = Time{hour: 0, minute: 0, second: 0.0};
pub const NOON: Time = Time{hour: 12, minute: 0, second: 0.0};

impl fmt::Display for Time {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    write!(f, "{:02}:{:02}:{:06.3}",
           self.hour, self.minute, self.second)
    }
}

impl Time {
    pub fn new(h: i32, m: i32, s: f64) -> Time {
        Time{
            hour: h,
            minute: m,
            second: s,
        }
    }

    pub fn frac(&self) -> f64 {
        (self.hour as f64 +
         self.minute as f64 / 60.0 +
         self.second / 3600.0) / 24.0
    }

    pub fn julian(&self) -> f64 {
        (self.frac() + 0.5) % 1.0
    }

    // more code follows
}

Other code can use this with

use astro::dts;

println!("{}", dts::Noon);

Testing this is done by putting tests at the end of the file:

// at EOF in dts.rs

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_time_frac() {
        assert!(NOON.frac() == 0.5,
            "noon should be halfway through the day");
        assert!(MIDNIGHT.frac() == 0.0,
            "midnight should be the start of the day");
    }

    #[test]
    fn test_time_julian() {
        assert!(MIDNIGHT.julian() == 0.5,
            "midnight should be halfway through the Julian day");
        assert!(NOON.julian() == 0.0,
            "noon should be the start of the Julian day");
    }
}

To expand this out, for example if dts expands, you can break it out into a subdirectory. Now you have src/dts/mod.rs, keeping pub mod dts in the top lib.rs.

Various floating point options

A bunch of small builtins:

  • abs
  • floor → FIXED
  • fract
  • min

etc.

Cargo registry

gitea supports a private Cargo registry. In an effort to avoid interacting with the Rust community as much as possible, I've decided to implement as much of what I need as I can. All it took for me was enabling the registry in gitea, and creating ~/.cargo/config.toml with

[registry]
default = "gitea"

[registries.gitea]
index = "sparse+https://git.example.com/api/packages/owner/cargo/" # Sparse index
token = "Bearer REDACTED"

The process of learning

I've picked up a couple of Rust books (and a course):

% tree -d Rust
Rust
├── Code Like a Pro in Rust
├── Idiomatic Rust
├── Refactoring to Rust
├── Rust in Action
└── Rust in Motion

What actually got me started was porting some of my existing code, and having a project I want to work on, then sitting down and just working on it.

Going forward

I have to start building some real things, not just math toys next. Maybe I'll build a CAS or KV store, or just dive right into RFC 5050.

Addendum

In addition to a Cargo registry, I also wanted Rust docs available too. While looking around for how to do this, I found kellnr. It took me a some effort to get it working; I found it easiest to install the Debian server version rather than Docker. I ran into a couple issues that were interesting to debug.

config.json not found

In one place, I had the registry path as /api/v1/cratesio/ instead of /api/v1/crates/ (I think I found some stale documentation somewhere). Well, turns out you can't find config.json if you're pointed at the wrong place. What threw me off at first was that the Gitea registry has an admin option to initialize the cargo.json, and I did that before publishing. Not sure if causation or correlation, but I couldn't find anything like that in Kellnr. Anyways, my cargo config looks something like this now:

[registry]
default = "kellnr"

[registries.kellnr]
index = "sparse+https://cargo.wntrmute.dev/api/v1/crates/"
credential-provider = ["cargo:token"]
token = "REDACTED"

[registries.gitea]
index = "sparse+https://git.wntrmute.dev/api/packages/wntrmute/cargo/" # Sparse index
# index = "https://git.wntrmute.dev/wntrmute/_cargo-index.git" # Git
token = "Bearer REDACTED"

docs not building

The doc problem was trickier, and essentially came down to the server user not having cargo in the path. Docs would be queued for building, they would show up in the data directory's queued directory, but never actually make it. I had the logging turned all the way up, including the web framework—which drowned out the log messages I needed to see.

Finally, it works

Now I have a cargo registry and rustdocs site that requires authentication to view and download packages.


Tags: