Skip to main content

zad_cli/cli/
man.rs

1//! Implementation of the `zad man [command]` subcommand mandated by
2//! `OSS_SPEC.md` §12.3.
3//!
4//! Manpages under `man/*.md` are embedded via `include_str!`. The
5//! command-to-file mapping is enumerated explicitly so the parity test
6//! in `tests/manpage_parity_test.rs` can cross-check it against the
7//! clap tree.
8//!
9//! Paths resolve through the `crates/zad-cli/man` symlink, which
10//! points at `../../man` (the canonical manpage tree at the repo
11//! root). The symlink is resolved when cargo packages the crate, so
12//! the published tarball ships the manpage files as real entries and
13//! the `cargo publish` verify step can compile the package outside
14//! the workspace. Don't replace the symlink with copies — that would
15//! immediately drift from the canonical manpages.
16
17use std::fmt::Write as _;
18
19use clap::Args;
20
21use zad::error::{Result, ZadError};
22
23#[derive(Debug, Args)]
24pub struct ManArgs {
25    /// Command name (e.g. `discord`, `service`). When omitted, lists the
26    /// available manpages. `main` is the top-level overview.
27    pub command: Option<String>,
28}
29
30pub const PAGES: &[(&str, &str)] = &[
31    ("main", include_str!("../../man/main.md")),
32    ("1pass", include_str!("../../man/1pass.md")),
33    ("commands", include_str!("../../man/commands.md")),
34    ("discord", include_str!("../../man/discord.md")),
35    ("docs", include_str!("../../man/docs.md")),
36    ("gcal", include_str!("../../man/gcal.md")),
37    ("man", include_str!("../../man/man.md")),
38    ("service", include_str!("../../man/service.md")),
39    ("signing", include_str!("../../man/signing.md")),
40    ("slack", include_str!("../../man/slack.md")),
41    ("spotify", include_str!("../../man/spotify.md")),
42    ("telegram", include_str!("../../man/telegram.md")),
43    ("ymusic", include_str!("../../man/ymusic.md")),
44];
45
46pub fn run(args: ManArgs) -> Result<()> {
47    match args.command {
48        None => {
49            let mut out = String::new();
50            out.push_str("Available manpages (run `zad man <command>` to read):\n");
51            for (name, _) in PAGES {
52                let _ = writeln!(out, "  {name}");
53            }
54            print!("{out}");
55            Ok(())
56        }
57        Some(command) => match PAGES.iter().find(|(n, _)| *n == command) {
58            Some((_, body)) => {
59                print!("{body}");
60                Ok(())
61            }
62            None => Err(ZadError::Invalid(format!(
63                "no manpage for: `{command}`. Run `zad man` to list available manpages."
64            ))),
65        },
66    }
67}