From bb221c904f5639ce35cded2d2f5d0709b8f04a07 Mon Sep 17 00:00:00 2001 From: Leynos Date: Mon, 1 Sep 2025 02:58:57 +0100 Subject: [PATCH] Add CLI man page generation --- .github/workflows/release.yml | 25 +++++++++++++++++++++++++ Cargo.lock | 18 ++++++++++++++++++ Cargo.toml | 5 +++++ build.rs | 24 ++++++++++++++++++++++++ docs/cli.md | 9 +++++++++ docs/contents.md | 2 ++ src/cli.rs | 28 ++++++++++++++++++++++++++++ src/main.rs | 14 ++++++++++++-- 8 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 build.rs create mode 100644 docs/cli.md create mode 100644 src/cli.rs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..79336b77 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,25 @@ +name: Release + +on: + release: + types: [published] + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: write + env: + CARGO_TERM_COLOR: always + BUILD_PROFILE: release + steps: + - uses: actions/checkout@v5 + - name: Setup Rust + uses: leynos/shared-actions/.github/actions/setup-rust@c6559452842af6a83b83429129dccaf910e34562 + - name: Build + run: make release + - name: Upload release assets + uses: softprops/action-gh-release@v2 + with: + files: | + target/generated-man/wireframe.1 diff --git a/Cargo.lock b/Cargo.lock index ea44b9ce..c48f733d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -353,6 +353,16 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +[[package]] +name = "clap_mangen" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b4c3c54b30f0d9adcb47f25f61fcce35c4dd8916638c6b82fbd5f4fb4179e2" +dependencies = [ + "clap", + "roff", +] + [[package]] name = "cmake" version = "0.1.54" @@ -1730,6 +1740,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "roff" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3" + [[package]] name = "rstest" version = "0.18.2" @@ -2967,6 +2983,8 @@ dependencies = [ "async-trait", "bincode", "bytes", + "clap", + "clap_mangen", "cucumber", "dashmap", "futures", diff --git a/Cargo.toml b/Cargo.toml index 17ed773c..c42969e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ metrics = { version = "0.24.2", optional = true } metrics-exporter-prometheus = { version = "0.17.2", optional = true, features = ["http-listener"] } thiserror = "2.0.16" static_assertions = "1" +clap = { version = "4.5", features = ["derive"] } [dev-dependencies] rstest = "0.26.1" @@ -63,6 +64,10 @@ tokio = { version = "1.47.1", default-features = false, features = [ "test-util", ] } +[build-dependencies] +clap = { version = "4.5", features = ["derive"] } +clap_mangen = "0.2" + [features] default = ["metrics"] metrics = ["dep:metrics", "dep:metrics-exporter-prometheus"] diff --git a/build.rs b/build.rs new file mode 100644 index 00000000..9698b73e --- /dev/null +++ b/build.rs @@ -0,0 +1,24 @@ +//! Build script generating manual pages from the CLI definition. + +use std::{fs, path::PathBuf}; + +use clap::CommandFactory; +use clap_mangen::Man; + +#[path = "src/cli.rs"] +mod cli; + +fn main() -> Result<(), Box> { + println!("cargo:rerun-if-changed=src/cli.rs"); + + let out_dir = PathBuf::from("target/generated-man"); + fs::create_dir_all(&out_dir)?; + + let cmd = cli::Cli::command(); + let man = Man::new(cmd); + let mut buf: Vec = Vec::new(); + man.render(&mut buf)?; + fs::write(out_dir.join("wireframe.1"), buf)?; + + Ok(()) +} diff --git a/docs/cli.md b/docs/cli.md new file mode 100644 index 00000000..706685f2 --- /dev/null +++ b/docs/cli.md @@ -0,0 +1,9 @@ +# Command line interface + +Wireframe includes a small command line interface for demonstration. The CLI +uses `clap` to parse arguments. An optional `--name` flag allows customising +the greeting printed by the `wireframe` binary. + +Manual pages are generated during the build via `clap_mangen`. The `build.rs` +script writes `wireframe.1` to `target/generated-man`, and the `release` GitHub +workflow uploads this file. diff --git a/docs/contents.md b/docs/contents.md index ee94c1e4..9c990aee 100644 --- a/docs/contents.md +++ b/docs/contents.md @@ -61,3 +61,5 @@ the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md writing project documentation. - [Server configuration](server/configuration.md) Tuning accept loop backoff behaviour and builder options. +- [Command line interface](cli.md) Overview of CLI usage and man page + generation. diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 00000000..e0c96274 --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,28 @@ +//! Command line interface for the wireframe example binary. +//! +//! Provides a tiny CLI to demonstrate argument parsing and man page +//! generation. + +use clap::Parser; + +/// Command line arguments for the `wireframe` binary. +#[derive(Debug, Parser)] +#[command(name = "wireframe", version, about = "Example Wireframe binary")] +pub struct Cli { + /// Name to greet. + #[arg(short, long)] + pub name: Option, +} + +#[cfg(test)] +mod tests { + use clap::Parser; + + use super::Cli; + + #[test] + fn parses_name_option() { + let cli = Cli::parse_from(["wireframe", "--name", "Sam"]); + assert_eq!(cli.name.as_deref(), Some("Sam")); + } +} diff --git a/src/main.rs b/src/main.rs index c73477d4..7fb82910 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,20 @@ //! Minimal binary demonstrating `wireframe` usage. //! -//! Currently prints a greeting and exits. +//! Parses CLI arguments and prints a greeting. + +mod cli; + +use clap::Parser; fn main() { // Enable structured logging for examples and integration tests. // Applications embedding the library should install their own subscriber. tracing_subscriber::fmt::init(); - println!("Hello from Wireframe!"); + + let cli = cli::Cli::parse(); + if let Some(name) = cli.name { + println!("Hello, {name} from Wireframe!"); + } else { + println!("Hello from Wireframe!"); + } }