use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(
name = "cartomancer",
version,
about = "PR review with blast radius awareness"
)]
pub struct Cli {
#[command(subcommand)]
pub command: Command,
#[arg(long, env = "CARTOMANCER_CONFIG", default_value = ".cartomancer.toml")]
pub config: String,
}
#[derive(Subcommand)]
pub enum Command {
Scan {
#[arg(default_value = ".")]
path: String,
#[arg(long, default_value = "text")]
format: OutputFormat,
},
Serve {
#[arg(long, env = "CARTOMANCER_PORT", default_value = "3000")]
port: u16,
},
Review {
repo: String,
pr: u64,
#[arg(long)]
work_dir: Option<String>,
#[arg(long)]
dry_run: bool,
#[arg(long, default_value = "text")]
format: OutputFormat,
},
}
#[derive(Debug, Clone, clap::ValueEnum)]
pub enum OutputFormat {
Text,
Json,
}
#[cfg(test)]
mod tests {
use super::*;
use clap::Parser;
#[test]
fn cli_parse_review_minimal() {
let cli = Cli::try_parse_from(["cartomancer", "review", "owner/repo", "42"]).unwrap();
match cli.command {
Command::Review {
repo,
pr,
work_dir,
dry_run,
..
} => {
assert_eq!(repo, "owner/repo");
assert_eq!(pr, 42);
assert!(work_dir.is_none());
assert!(!dry_run);
}
_ => panic!("expected Review command"),
}
}
#[test]
fn cli_parse_review_with_all_flags() {
let cli = Cli::try_parse_from([
"cartomancer",
"review",
"owner/repo",
"7",
"--work-dir",
"/tmp/repo",
"--dry-run",
"--format",
"json",
])
.unwrap();
match cli.command {
Command::Review {
repo,
pr,
work_dir,
dry_run,
format,
} => {
assert_eq!(repo, "owner/repo");
assert_eq!(pr, 7);
assert_eq!(work_dir.as_deref(), Some("/tmp/repo"));
assert!(dry_run);
assert!(matches!(format, OutputFormat::Json));
}
_ => panic!("expected Review command"),
}
}
#[test]
fn cli_parse_scan_still_works() {
let cli = Cli::try_parse_from(["cartomancer", "scan", ".", "--format", "json"]).unwrap();
assert!(matches!(cli.command, Command::Scan { .. }));
}
#[test]
fn cli_review_repo_is_positional() {
let result = Cli::try_parse_from(["cartomancer", "review", "--repo", "a/b", "--pr", "1"]);
assert!(result.is_err());
}
}