Skip to main content

zad_cli/cli/
docs.rs

1//! Implementation of the `zad docs [topic]` subcommand mandated by
2//! `OSS_SPEC.md` ยง12.3.
3//!
4//! Every topic doc under `docs/*.md` is embedded into the binary via
5//! `include_str!` so the exact text a contributor ships is the exact
6//! text an agent sees at runtime โ€” no filesystem lookups, no version
7//! skew between the installed binary and the source tree.
8//!
9//! Paths resolve through the `crates/zad-cli/docs` symlink, which
10//! points at `../../docs` (the canonical docs tree at the repo root).
11//! The symlink is resolved when cargo packages the crate, so the
12//! published tarball ships the doc files as real entries and the
13//! `cargo publish` verify step can compile the package outside the
14//! workspace. Don't replace the symlink with copies โ€” that would
15//! immediately drift from the canonical docs.
16
17use std::fmt::Write as _;
18
19use clap::Args;
20
21use zad::error::{Result, ZadError};
22
23#[derive(Debug, Args)]
24pub struct DocsArgs {
25    /// Topic name (without the `.md` extension). When omitted, lists the
26    /// available topics.
27    pub topic: Option<String>,
28}
29
30const TOPICS: &[(&str, &str)] = &[
31    ("architecture", include_str!("../../docs/architecture.md")),
32    ("configuration", include_str!("../../docs/configuration.md")),
33    (
34        "getting-started",
35        include_str!("../../docs/getting-started.md"),
36    ),
37    (
38        "troubleshooting",
39        include_str!("../../docs/troubleshooting.md"),
40    ),
41];
42
43pub fn run(args: DocsArgs) -> Result<()> {
44    match args.topic {
45        None => {
46            let mut out = String::new();
47            out.push_str("Available topics (run `zad docs <topic>` to read):\n");
48            for (name, _) in TOPICS {
49                let _ = writeln!(out, "  {name}");
50            }
51            print!("{out}");
52            Ok(())
53        }
54        Some(topic) => match TOPICS.iter().find(|(n, _)| *n == topic) {
55            Some((_, body)) => {
56                print!("{body}");
57                Ok(())
58            }
59            None => Err(ZadError::Invalid(format!(
60                "no such docs topic: `{topic}`. Run `zad docs` to list available topics."
61            ))),
62        },
63    }
64}