crates/gunnar-cli/src/main.rs
//! The `gunnar` binary.
//!
//! Parsing lives in [`gunnar_cli::cli`], the verbs in `gunnar_cli::server` and
//! `gunnar_cli::client`, and the dispatch itself in [`gunnar_cli::run`] — this
//! file is the process entry point and nothing else, so every verb is reachable
//! from a test without spawning a subprocess, and a binary that first installs a
//! cold tier (`crates/gunnar-znippy`) reuses the dispatch instead of copying it.
//!
//! # The build-thing receipt lives here, and only here
//!
//! `build.rs` writes a `#[used] #[link_section = ".nordisk.buildthing"]` static
//! naming the recipe that built this binary and every setting it resolved;
//! the line below is what puts it in the compilation. dwarves reads it back with
//! an object parser, never by running the artifact — which is why it works on a
//! cross-built musl binary, on a stripped one, and on a hostile one.
//!
//! **In `main.rs` and not in `lib.rs`, deliberately.** Two copies of the static
//! in one artifact concatenate in the section, and
//! `nornir_build_thing::receipt::decode` reports that as "more than one receipt"
//! rather than guessing which one built it — correct, and a failure nobody wants
//! to meet. Every one of this package's six `[[bin]]` targets compiles this same
//! file, so all six carry the receipt and none carries two.
//!
//! The path comes from the env var `build.rs` published rather than from a name
//! spelled here, so the file is named in exactly one place — the writer.
include!(env!("GUNNAR_BUILD_RECEIPT"));
/// The heap, for the builds of this file that serve.
///
/// `#[cfg(feature = "server")]` for the same reason the receipt note above
/// gives: this one file is compiled by all six `[[bin]]` targets. `gunnar-solo`
/// and `gunnar-service` serve clones and want
/// [`gunnar_server::ServerAllocator`]; the client-only `gunnar` does not link
/// `gunnar-server` at all, and a CLI that runs for 40 ms has nothing to win
/// from an allocator built for 32 concurrent uploads.
#[cfg(feature = "server")]
#[global_allocator]
static ALLOC: gunnar_server::ServerAllocator = gunnar_server::SERVER_ALLOCATOR;
fn main() {
let outcome = gunnar_cli::run();
// The console queue (`gunnar_common::logqueue`) is drained by its own
// thread when stderr is not a terminal; a return from `main` would take
// whatever it still holds.
gunnar_common::logqueue::flush(std::time::Duration::from_secs(5));
if let Err(err) = outcome {
// `{err:#}` prints the whole anyhow chain. A bare `{err}` would show
// "refusing to serve" and swallow the sentence that says what to do
// about it.
eprintln!("gunnar: {err:#}");
std::process::exit(1);
}
}