1use clap::Parser;
2
3#[derive(Parser, Debug, Clone)]
5#[clap(author, version, about)]
6pub struct Args {
7 #[clap(short = 'd', long, default_value = ".")]
9 pub input: String,
10
11 #[clap(short, long, default_value = "output.md")]
13 pub output: String,
14
15 #[clap(short = 'f', long, value_delimiter = ',')]
17 pub filter: Vec<String>,
18
19 #[clap(short = 'i', long)]
21 pub ignore: Vec<String>,
22
23 #[clap(long)]
25 pub preview: bool,
26
27 #[clap(long)]
29 pub token_count: bool,
30
31 #[clap(long)]
33 pub line_numbers: bool,
34
35 #[clap(short = 'y', long)]
37 pub yes: bool,
38
39 #[clap(long)]
41 pub max_tokens: Option<usize>,
42
43 #[clap(long, default_value_t = false)]
45 pub diff_only: bool,
46
47 #[clap(long)]
49 pub clear_cache: bool,
50
51 #[clap(long)]
53 pub init: bool,
54
55 #[clap(long)]
57 pub signatures: bool,
58
59 #[clap(long)]
61 pub structure: bool,
62
63 #[clap(long, value_name = "MODE", default_value = "smart")]
65 pub truncate: String,
66
67 #[clap(long, default_value = "all")]
69 pub visibility: String,
70}
71
72#[cfg(test)]
73mod tests {
74 use super::Args;
75 use clap::Parser;
76
77 #[test]
78 fn parses_with_no_args() {
79 let res = Args::try_parse_from(["context-builder"]);
80 assert!(res.is_ok(), "Expected success when no args are provided");
81 }
82
83 #[test]
84 fn parses_all_flags_and_options() {
85 let args = Args::try_parse_from([
86 "context-builder",
87 "--input",
88 "some/dir",
89 "--output",
90 "ctx.md",
91 "--filter",
92 "rs",
93 "--filter",
94 "toml",
95 "--ignore",
96 "target",
97 "--ignore",
98 "node_modules",
99 "--preview",
100 "--token-count",
101 "--line-numbers",
102 "--diff-only",
103 "--clear-cache",
104 ])
105 .expect("should parse");
106
107 assert_eq!(args.input, "some/dir");
108 assert_eq!(args.output, "ctx.md");
109 assert_eq!(args.filter, vec!["rs".to_string(), "toml".to_string()]);
110 assert_eq!(
111 args.ignore,
112 vec!["target".to_string(), "node_modules".to_string()]
113 );
114 assert!(args.preview);
115 assert!(args.token_count);
116 assert!(args.line_numbers);
117 assert!(args.diff_only);
118 assert!(args.clear_cache);
119 }
120
121 #[test]
122 fn short_flags_parse_correctly() {
123 let args = Args::try_parse_from([
124 "context-builder",
125 "-d",
126 ".",
127 "-o",
128 "out.md",
129 "-f",
130 "md",
131 "-f",
132 "rs",
133 "-i",
134 "target",
135 "-i",
136 ".git",
137 ])
138 .expect("should parse");
139
140 assert_eq!(args.input, ".");
141 assert_eq!(args.output, "out.md");
142 assert_eq!(args.filter, vec!["md".to_string(), "rs".to_string()]);
143 assert_eq!(args.ignore, vec!["target".to_string(), ".git".to_string()]);
144 assert!(!args.preview);
145 assert!(!args.line_numbers);
146 assert!(!args.clear_cache);
147 }
148
149 #[test]
150 fn defaults_for_options_when_not_provided() {
151 let args = Args::try_parse_from(["context-builder", "-d", "proj"]).expect("should parse");
152
153 assert_eq!(args.input, "proj");
154 assert_eq!(args.output, "output.md");
155 assert!(args.filter.is_empty());
156 assert!(args.ignore.is_empty());
157 assert!(!args.preview);
158 assert!(!args.line_numbers);
159 assert!(!args.diff_only);
160 assert!(!args.clear_cache);
161 }
162
163 #[test]
164 fn parses_diff_only_flag() {
165 let args = Args::try_parse_from(["context-builder", "--diff-only"])
166 .expect("should parse diff-only flag");
167 assert!(args.diff_only);
168 assert!(!args.clear_cache);
169 }
170
171 #[test]
172 fn parses_clear_cache_flag() {
173 let args = Args::try_parse_from(["context-builder", "--clear-cache"])
174 .expect("should parse clear-cache flag");
175 assert!(args.clear_cache);
176 assert!(!args.diff_only);
177 }
178
179 #[test]
180 fn parses_signatures_flag() {
181 let args = Args::try_parse_from(["context-builder", "--signatures"])
182 .expect("should parse signatures flag");
183 assert!(args.signatures);
184 }
185
186 #[test]
187 fn parses_structure_flag() {
188 let args = Args::try_parse_from(["context-builder", "--structure"])
189 .expect("should parse structure flag");
190 assert!(args.structure);
191 }
192
193 #[test]
194 fn parses_truncate_mode() {
195 let args = Args::try_parse_from(["context-builder", "--truncate", "byte"])
196 .expect("should parse truncate flag");
197 assert_eq!(args.truncate, "byte");
198
199 let args_default =
200 Args::try_parse_from(["context-builder"]).expect("should parse with default truncate");
201 assert_eq!(args_default.truncate, "smart");
202 }
203
204 #[test]
205 fn parses_visibility_filter() {
206 let args = Args::try_parse_from(["context-builder", "--visibility", "public"])
207 .expect("should parse visibility flag");
208 assert_eq!(args.visibility, "public");
209
210 let args_default = Args::try_parse_from(["context-builder"])
211 .expect("should parse with default visibility");
212 assert_eq!(args_default.visibility, "all");
213 }
214}