communique 1.5.0

Editorialized release notes powered by AI
use crate::cli::Cli;

/// Generates a usage spec for the CLI
///
/// https://usage.jdx.dev
#[derive(Debug, usage_rs::Args)]
#[usage(effect = "read")]
pub struct Usage;

/// The spec written to communique.usage.kdl, shared by `communique usage` and
/// the test that keeps the checked-in copy in sync.
fn generate_to_string() -> String {
    format!(
        "// @generated by `communique usage`\n{}",
        Cli::to_kdl().trim_end()
    )
}

impl Usage {
    pub fn run(&self) -> miette::Result<()> {
        println!("{}", generate_to_string());
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    /// `run` is thin, but it is the only path that actually emits the spec —
    /// leaving it untested meant nothing checked that `communique usage`
    /// succeeds at all.
    #[test]
    fn run_succeeds() {
        assert!(Usage {}.run().is_ok());
    }

    #[test]
    fn test_usage_kdl_in_sync() {
        let generated = generate_to_string();
        let on_disk = std::fs::read_to_string(
            std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("communique.usage.kdl"),
        )
        .expect("communique.usage.kdl should exist");
        assert_eq!(
            generated.trim(),
            on_disk.trim(),
            "communique.usage.kdl is out of sync — run `cargo run -- usage > communique.usage.kdl` to update"
        );
    }

    /// The facade metadata is the source of the emitted spec, so inspect that
    /// metadata directly instead of reparsing KDL through usage-lib.
    #[test]
    fn the_spec_is_a_spec() {
        let spec = Cli::spec();
        assert_eq!(spec.bin, Some("communique"));
        let command = |name: &str| {
            spec.root
                .subcommands
                .iter()
                .copied()
                .find(|command| command.cmd.name == name)
                .unwrap()
        };

        assert_eq!(
            command("init").extra.effect,
            Some(usage_rs::spec::Effect::Write)
        );
        assert_eq!(
            command("generate").extra.effect,
            Some(usage_rs::spec::Effect::Read)
        );
        assert_eq!(
            command("sponsors").extra.effect,
            Some(usage_rs::spec::Effect::Read)
        );

        let generate = command("generate");
        let init = command("init");
        assert_eq!(
            generate
                .flags
                .iter()
                .find(|flag| flag.flag.longs.contains(&"github-release"))
                .expect("--github-release")
                .extra
                .effect,
            Some(usage_rs::spec::Effect::Write),
            "a flag raises what its command does"
        );
        assert_eq!(
            init.flags
                .iter()
                .find(|flag| flag.flag.longs.contains(&"force"))
                .expect("--force")
                .extra
                .effect,
            Some(usage_rs::spec::Effect::Destructive)
        );
    }
}