1use clap::{Parser, Subcommand, ValueEnum};
4
5#[derive(Parser, Debug)]
7#[command(name = "adrscope")]
8#[command(author, version, about, long_about = None)]
9pub struct Cli {
10 #[arg(short, long, global = true)]
12 pub verbose: bool,
13
14 #[command(subcommand)]
16 pub command: Commands,
17}
18
19#[derive(Subcommand, Debug)]
21pub enum Commands {
22 Generate(GenerateArgs),
24
25 Wiki(WikiArgs),
27
28 Validate(ValidateArgs),
30
31 Stats(StatsArgs),
33}
34
35#[derive(Parser, Debug)]
37pub struct GenerateArgs {
38 #[arg(short, long, default_value = "docs/decisions")]
40 pub input: String,
41
42 #[arg(short, long, default_value = "adrs.html")]
44 pub output: String,
45
46 #[arg(short, long, default_value = "Architecture Decision Records")]
48 pub title: String,
49
50 #[arg(long, value_enum, default_value = "auto")]
52 pub theme: ThemeArg,
53
54 #[arg(short, long, default_value = "**/*.md")]
56 pub pattern: String,
57}
58
59#[derive(Parser, Debug)]
61pub struct WikiArgs {
62 #[arg(short, long, default_value = "docs/decisions")]
64 pub input: String,
65
66 #[arg(short, long, default_value = "wiki")]
68 pub output: String,
69
70 #[arg(long)]
72 pub pages_url: Option<String>,
73
74 #[arg(short, long, default_value = "**/*.md")]
76 pub pattern: String,
77}
78
79#[derive(Parser, Debug)]
81pub struct ValidateArgs {
82 #[arg(short, long, default_value = "docs/decisions")]
84 pub input: String,
85
86 #[arg(short, long, default_value = "**/*.md")]
88 pub pattern: String,
89
90 #[arg(long)]
92 pub strict: bool,
93}
94
95#[derive(Parser, Debug)]
97pub struct StatsArgs {
98 #[arg(short, long, default_value = "docs/decisions")]
100 pub input: String,
101
102 #[arg(short, long, default_value = "**/*.md")]
104 pub pattern: String,
105
106 #[arg(short, long, value_enum, default_value = "text")]
108 pub format: FormatArg,
109}
110
111#[derive(ValueEnum, Clone, Debug, Default)]
113pub enum ThemeArg {
114 Light,
116 Dark,
118 #[default]
120 Auto,
121}
122
123impl From<ThemeArg> for crate::infrastructure::Theme {
124 fn from(arg: ThemeArg) -> Self {
125 match arg {
126 ThemeArg::Light => Self::Light,
127 ThemeArg::Dark => Self::Dark,
128 ThemeArg::Auto => Self::Auto,
129 }
130 }
131}
132
133#[derive(ValueEnum, Clone, Debug, Default)]
135pub enum FormatArg {
136 #[default]
138 Text,
139 Json,
141 Markdown,
143}
144
145impl From<FormatArg> for crate::application::stats::StatsFormat {
146 fn from(arg: FormatArg) -> Self {
147 match arg {
148 FormatArg::Text => Self::Text,
149 FormatArg::Json => Self::Json,
150 FormatArg::Markdown => Self::Markdown,
151 }
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158 use clap::CommandFactory;
159
160 #[test]
161 fn test_cli_parses() {
162 Cli::command().debug_assert();
164 }
165
166 #[test]
167 fn test_generate_defaults() {
168 let args = GenerateArgs {
169 input: "docs/decisions".to_string(),
170 output: "adrs.html".to_string(),
171 title: "ADRs".to_string(),
172 theme: ThemeArg::Auto,
173 pattern: "**/*.md".to_string(),
174 };
175
176 assert_eq!(args.input, "docs/decisions");
177 assert_eq!(args.output, "adrs.html");
178 }
179
180 #[test]
181 fn test_theme_conversion() {
182 use crate::infrastructure::Theme;
183
184 assert!(matches!(Theme::from(ThemeArg::Light), Theme::Light));
185 assert!(matches!(Theme::from(ThemeArg::Dark), Theme::Dark));
186 assert!(matches!(Theme::from(ThemeArg::Auto), Theme::Auto));
187 }
188
189 #[test]
190 fn test_format_conversion() {
191 use crate::application::stats::StatsFormat;
192
193 assert!(matches!(
194 StatsFormat::from(FormatArg::Text),
195 StatsFormat::Text
196 ));
197 assert!(matches!(
198 StatsFormat::from(FormatArg::Json),
199 StatsFormat::Json
200 ));
201 assert!(matches!(
202 StatsFormat::from(FormatArg::Markdown),
203 StatsFormat::Markdown
204 ));
205 }
206}