1use crate::{SpecArg, SpecCommand, SpecFlag};
34
35#[derive(Debug, Default, Clone)]
37pub struct SpecFlagBuilder {
38 inner: SpecFlag,
39}
40
41impl SpecFlagBuilder {
42 pub fn new() -> Self {
44 Self::default()
45 }
46
47 pub fn name(mut self, name: impl Into<String>) -> Self {
49 self.inner.name = name.into();
50 self
51 }
52
53 pub fn short(mut self, c: char) -> Self {
55 self.inner.short.push(c);
56 self
57 }
58
59 pub fn shorts(mut self, chars: impl IntoIterator<Item = char>) -> Self {
61 self.inner.short.extend(chars);
62 self
63 }
64
65 pub fn long(mut self, name: impl Into<String>) -> Self {
67 self.inner.long.push(name.into());
68 self
69 }
70
71 pub fn longs<I, S>(mut self, names: I) -> Self
73 where
74 I: IntoIterator<Item = S>,
75 S: Into<String>,
76 {
77 self.inner.long.extend(names.into_iter().map(Into::into));
78 self
79 }
80
81 pub fn default_value(mut self, value: impl Into<String>) -> Self {
83 self.inner.default.push(value.into());
84 self.inner.required = false;
85 self
86 }
87
88 pub fn default_values<I, S>(mut self, values: I) -> Self
90 where
91 I: IntoIterator<Item = S>,
92 S: Into<String>,
93 {
94 self.inner
95 .default
96 .extend(values.into_iter().map(Into::into));
97 if !self.inner.default.is_empty() {
98 self.inner.required = false;
99 }
100 self
101 }
102
103 pub fn help(mut self, text: impl Into<String>) -> Self {
105 self.inner.help = Some(text.into());
106 self
107 }
108
109 pub fn help_long(mut self, text: impl Into<String>) -> Self {
111 self.inner.help_long = Some(text.into());
112 self
113 }
114
115 pub fn help_md(mut self, text: impl Into<String>) -> Self {
117 self.inner.help_md = Some(text.into());
118 self
119 }
120
121 pub fn var(mut self, is_var: bool) -> Self {
123 self.inner.var = is_var;
124 self
125 }
126
127 pub fn var_min(mut self, min: usize) -> Self {
129 self.inner.var_min = Some(min);
130 self
131 }
132
133 pub fn var_max(mut self, max: usize) -> Self {
135 self.inner.var_max = Some(max);
136 self
137 }
138
139 pub fn required(mut self, is_required: bool) -> Self {
141 self.inner.required = is_required;
142 self
143 }
144
145 pub fn global(mut self, is_global: bool) -> Self {
147 self.inner.global = is_global;
148 self
149 }
150
151 pub fn hide(mut self, is_hidden: bool) -> Self {
153 self.inner.hide = is_hidden;
154 self
155 }
156
157 pub fn count(mut self, is_count: bool) -> Self {
159 self.inner.count = is_count;
160 self
161 }
162
163 pub fn arg(mut self, arg: SpecArg) -> Self {
165 self.inner.arg = Some(arg);
166 self
167 }
168
169 pub fn negate(mut self, negate: impl Into<String>) -> Self {
171 self.inner.negate = Some(negate.into());
172 self
173 }
174
175 pub fn env(mut self, env: impl Into<String>) -> Self {
177 self.inner.env = Some(env.into());
178 self
179 }
180
181 pub fn deprecated(mut self, msg: impl Into<String>) -> Self {
183 self.inner.deprecated = Some(msg.into());
184 self
185 }
186
187 pub fn build(mut self) -> SpecFlag {
189 self.inner.usage = self.inner.usage();
190 if self.inner.name.is_empty() {
191 if let Some(long) = self.inner.long.first() {
193 self.inner.name = long.clone();
194 } else if let Some(short) = self.inner.short.first() {
195 self.inner.name = short.to_string();
196 }
197 }
198 self.inner
199 }
200}
201
202#[derive(Debug, Default, Clone)]
204pub struct SpecArgBuilder {
205 inner: SpecArg,
206}
207
208impl SpecArgBuilder {
209 pub fn new() -> Self {
211 Self::default()
212 }
213
214 pub fn name(mut self, name: impl Into<String>) -> Self {
216 self.inner.name = name.into();
217 self
218 }
219
220 pub fn default_value(mut self, value: impl Into<String>) -> Self {
222 self.inner.default.push(value.into());
223 self.inner.required = false;
224 self
225 }
226
227 pub fn default_values<I, S>(mut self, values: I) -> Self
229 where
230 I: IntoIterator<Item = S>,
231 S: Into<String>,
232 {
233 self.inner
234 .default
235 .extend(values.into_iter().map(Into::into));
236 if !self.inner.default.is_empty() {
237 self.inner.required = false;
238 }
239 self
240 }
241
242 pub fn help(mut self, text: impl Into<String>) -> Self {
244 self.inner.help = Some(text.into());
245 self
246 }
247
248 pub fn help_long(mut self, text: impl Into<String>) -> Self {
250 self.inner.help_long = Some(text.into());
251 self
252 }
253
254 pub fn help_md(mut self, text: impl Into<String>) -> Self {
256 self.inner.help_md = Some(text.into());
257 self
258 }
259
260 pub fn var(mut self, is_var: bool) -> Self {
262 self.inner.var = is_var;
263 self
264 }
265
266 pub fn var_min(mut self, min: usize) -> Self {
268 self.inner.var_min = Some(min);
269 self
270 }
271
272 pub fn var_max(mut self, max: usize) -> Self {
274 self.inner.var_max = Some(max);
275 self
276 }
277
278 pub fn required(mut self, is_required: bool) -> Self {
280 self.inner.required = is_required;
281 self
282 }
283
284 pub fn hide(mut self, is_hidden: bool) -> Self {
286 self.inner.hide = is_hidden;
287 self
288 }
289
290 pub fn env(mut self, env: impl Into<String>) -> Self {
292 self.inner.env = Some(env.into());
293 self
294 }
295
296 pub fn build(mut self) -> SpecArg {
298 self.inner.usage = self.inner.usage();
299 self.inner
300 }
301}
302
303#[derive(Debug, Default, Clone)]
305pub struct SpecCommandBuilder {
306 inner: SpecCommand,
307}
308
309impl SpecCommandBuilder {
310 pub fn new() -> Self {
312 Self::default()
313 }
314
315 pub fn name(mut self, name: impl Into<String>) -> Self {
317 self.inner.name = name.into();
318 self
319 }
320
321 pub fn alias(mut self, alias: impl Into<String>) -> Self {
323 self.inner.aliases.push(alias.into());
324 self
325 }
326
327 pub fn aliases<I, S>(mut self, aliases: I) -> Self
329 where
330 I: IntoIterator<Item = S>,
331 S: Into<String>,
332 {
333 self.inner
334 .aliases
335 .extend(aliases.into_iter().map(Into::into));
336 self
337 }
338
339 pub fn hidden_alias(mut self, alias: impl Into<String>) -> Self {
341 self.inner.hidden_aliases.push(alias.into());
342 self
343 }
344
345 pub fn hidden_aliases<I, S>(mut self, aliases: I) -> Self
347 where
348 I: IntoIterator<Item = S>,
349 S: Into<String>,
350 {
351 self.inner
352 .hidden_aliases
353 .extend(aliases.into_iter().map(Into::into));
354 self
355 }
356
357 pub fn flag(mut self, flag: SpecFlag) -> Self {
359 self.inner.flags.push(flag);
360 self
361 }
362
363 pub fn flags(mut self, flags: impl IntoIterator<Item = SpecFlag>) -> Self {
365 self.inner.flags.extend(flags);
366 self
367 }
368
369 pub fn arg(mut self, arg: SpecArg) -> Self {
371 self.inner.args.push(arg);
372 self
373 }
374
375 pub fn args(mut self, args: impl IntoIterator<Item = SpecArg>) -> Self {
377 self.inner.args.extend(args);
378 self
379 }
380
381 pub fn help(mut self, text: impl Into<String>) -> Self {
383 self.inner.help = Some(text.into());
384 self
385 }
386
387 pub fn help_long(mut self, text: impl Into<String>) -> Self {
389 self.inner.help_long = Some(text.into());
390 self
391 }
392
393 pub fn help_md(mut self, text: impl Into<String>) -> Self {
395 self.inner.help_md = Some(text.into());
396 self
397 }
398
399 pub fn hide(mut self, is_hidden: bool) -> Self {
401 self.inner.hide = is_hidden;
402 self
403 }
404
405 pub fn subcommand_required(mut self, required: bool) -> Self {
407 self.inner.subcommand_required = required;
408 self
409 }
410
411 pub fn deprecated(mut self, msg: impl Into<String>) -> Self {
413 self.inner.deprecated = Some(msg.into());
414 self
415 }
416
417 pub fn build(mut self) -> SpecCommand {
419 self.inner.usage = self.inner.usage();
420 self.inner
421 }
422}
423
424#[cfg(test)]
425mod tests {
426 use super::*;
427
428 #[test]
429 fn test_flag_builder_basic() {
430 let flag = SpecFlagBuilder::new()
431 .name("verbose")
432 .short('v')
433 .long("verbose")
434 .help("Enable verbose output")
435 .build();
436
437 assert_eq!(flag.name, "verbose");
438 assert_eq!(flag.short, vec!['v']);
439 assert_eq!(flag.long, vec!["verbose".to_string()]);
440 assert_eq!(flag.help, Some("Enable verbose output".to_string()));
441 }
442
443 #[test]
444 fn test_flag_builder_multiple_values() {
445 let flag = SpecFlagBuilder::new()
446 .shorts(['v', 'V'])
447 .longs(["verbose", "loud"])
448 .default_values(["info", "warn"])
449 .build();
450
451 assert_eq!(flag.short, vec!['v', 'V']);
452 assert_eq!(flag.long, vec!["verbose".to_string(), "loud".to_string()]);
453 assert_eq!(flag.default, vec!["info".to_string(), "warn".to_string()]);
454 assert!(!flag.required); }
456
457 #[test]
458 fn test_flag_builder_variadic() {
459 let flag = SpecFlagBuilder::new()
460 .long("file")
461 .var(true)
462 .var_min(1)
463 .var_max(10)
464 .build();
465
466 assert!(flag.var);
467 assert_eq!(flag.var_min, Some(1));
468 assert_eq!(flag.var_max, Some(10));
469 }
470
471 #[test]
472 fn test_flag_builder_name_derivation() {
473 let flag = SpecFlagBuilder::new().short('v').long("verbose").build();
474
475 assert_eq!(flag.name, "verbose");
477
478 let flag2 = SpecFlagBuilder::new().short('v').build();
479
480 assert_eq!(flag2.name, "v");
482 }
483
484 #[test]
485 fn test_arg_builder_basic() {
486 let arg = SpecArgBuilder::new()
487 .name("file")
488 .help("Input file")
489 .required(true)
490 .build();
491
492 assert_eq!(arg.name, "file");
493 assert_eq!(arg.help, Some("Input file".to_string()));
494 assert!(arg.required);
495 }
496
497 #[test]
498 fn test_arg_builder_variadic() {
499 let arg = SpecArgBuilder::new()
500 .name("files")
501 .var(true)
502 .var_min(1)
503 .var_max(10)
504 .help("Input files")
505 .build();
506
507 assert_eq!(arg.name, "files");
508 assert!(arg.var);
509 assert_eq!(arg.var_min, Some(1));
510 assert_eq!(arg.var_max, Some(10));
511 }
512
513 #[test]
514 fn test_arg_builder_defaults() {
515 let arg = SpecArgBuilder::new()
516 .name("file")
517 .default_values(["a.txt", "b.txt"])
518 .build();
519
520 assert_eq!(arg.default, vec!["a.txt".to_string(), "b.txt".to_string()]);
521 assert!(!arg.required);
522 }
523
524 #[test]
525 fn test_command_builder_basic() {
526 let cmd = SpecCommandBuilder::new()
527 .name("install")
528 .help("Install packages")
529 .build();
530
531 assert_eq!(cmd.name, "install");
532 assert_eq!(cmd.help, Some("Install packages".to_string()));
533 }
534
535 #[test]
536 fn test_command_builder_aliases() {
537 let cmd = SpecCommandBuilder::new()
538 .name("install")
539 .alias("i")
540 .aliases(["add", "get"])
541 .hidden_aliases(["inst"])
542 .build();
543
544 assert_eq!(
545 cmd.aliases,
546 vec!["i".to_string(), "add".to_string(), "get".to_string()]
547 );
548 assert_eq!(cmd.hidden_aliases, vec!["inst".to_string()]);
549 }
550
551 #[test]
552 fn test_command_builder_with_flags_and_args() {
553 let flag = SpecFlagBuilder::new().short('f').long("force").build();
554
555 let arg = SpecArgBuilder::new().name("package").required(true).build();
556
557 let cmd = SpecCommandBuilder::new()
558 .name("install")
559 .flag(flag)
560 .arg(arg)
561 .build();
562
563 assert_eq!(cmd.flags.len(), 1);
564 assert_eq!(cmd.flags[0].name, "force");
565 assert_eq!(cmd.args.len(), 1);
566 assert_eq!(cmd.args[0].name, "package");
567 }
568}