Carl Worth [Tue, 10 Mar 2026 14:56:54 +0000 (07:56 -0700)]
Use expect instead of unwrap_or_else
I just learned about this convenient method provided by Result.
Compared to using unwrap_or_else, I don't need to explicitly call
std::process::exit() and compared to using ? I still get to control
the error string (to include the filename in it).
Carl Worth [Tue, 10 Mar 2026 06:11:05 +0000 (23:11 -0700)]
Add a single route at /race
That returns the complete race state, which, for now, is only the name
of the race.
New rust features seen for the first time as of this commit:
* std::sync::Arc for atomically-reference-counted pointers (with
thread-safe reference counting as you would expect from the name).
* "crate::" in 'use' paths to reference the root of the current project
* impl: For creating functions associated with a struct
* Destructuring in a function parameter: State(state): State<Arc<AppState>>
* clone() to carefully give the RaceResponse a string it
owns. (Note: The rust compiler wouldn't even accept the code
without this clone(): "cannot move out of an `Arc`)
Carl Worth [Tue, 10 Mar 2026 05:20:10 +0000 (22:20 -0700)]
Add dependencies on axum and tokio and implement a do-nothing server
This server binds to port 3000 on all interfaces and then just listens,
(with no routes configured to do anything).
The axum library gives us the server and router, (which, like I said,
we aren't using yet). And tokio gives us some network functionality
(see the TCP listener and the bind function) as well as some
asynchronous functionality, (see the #[tokio::main] attribute which
saves bunch of boilerplate that would otherwise be needed (creating a
tokio::runtime::Builder and giving it a function to run), and instead
lets us just use the entire body of main as the async function body
that tokio launches.
Carl Worth [Tue, 10 Mar 2026 02:36:37 +0000 (19:36 -0700)]
Parse the json file and deserialize it into a struct
Where the struct only has a 'name' field so far.
This commit shows a few new things in rust that we haven't seen
previously:
* The "mod config" to depend on a module contained in config.rs
* The ? operator ("try operator") which unwraps a Result if it is_ok,
and insteadd returns an error if it is not.
* Dependencies on rust packages, (serde_json, and serde with the
"derive" feature") used for the JSON parsing and deserializing.
* The derive attribute (spelled "#[derive(...)]" used to automatically
create implementations of the Debug and Deserialize traits for our
struct. Debug isn't used here, but could be used by passing a
ConfigFile value to println! or format! macros using a "{:?}" format
specifier.
* A closure accepting a single parameter: |e| { ... }
Carl Worth [Tue, 10 Mar 2026 01:52:58 +0000 (18:52 -0700)]
Return an actual value from main
This is a more idiomatic behavior from main, using a Result to return
either the unit type (which will cause a return of 0) or else an Error
(coerced from a string by using .into()). In that case, the program
will print "Error: " and our error string before exiting with a value
of 1.
Carl Worth [Tue, 10 Mar 2026 00:04:39 +0000 (17:04 -0700)]
Switch from hello world to a program that prints a usage string
OK, so it's not much more than "hello, world", but it just show how to
use std::env as well as Vec<String> and even has an actual condition
in it! It's almost like real programming now.
Oh, and of course there's the collect() call which showcases both an
iterator and the magic of rust's type inference here (telling
collect() to return a Vec<string>). So, there's quite a bit of rust
here, even for just a tiny block of code.
Carl Worth [Tue, 10 Mar 2026 00:02:57 +0000 (17:02 -0700)]
Rename package from "server" to "chip-timing-server"
The "server" name was appropriate for the directory we are in,
(underneath the chip-timing directory), but it's excessively vague for
an actual compiled binary name. So use chip-timing-server instead.