1use crate::{spec::arg::SpecDoubleDashChoices, 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 #[must_use]
189 pub fn build(mut self) -> SpecFlag {
190 self.inner.usage = self.inner.usage();
191 if self.inner.name.is_empty() {
192 if let Some(long) = self.inner.long.first() {
194 self.inner.name = long.clone();
195 } else if let Some(short) = self.inner.short.first() {
196 self.inner.name = short.to_string();
197 }
198 }
199 self.inner
200 }
201}
202
203#[derive(Debug, Default, Clone)]
205pub struct SpecArgBuilder {
206 inner: SpecArg,
207}
208
209impl SpecArgBuilder {
210 pub fn new() -> Self {
212 Self::default()
213 }
214
215 pub fn name(mut self, name: impl Into<String>) -> Self {
217 self.inner.name = name.into();
218 self
219 }
220
221 pub fn default_value(mut self, value: impl Into<String>) -> Self {
223 self.inner.default.push(value.into());
224 self.inner.required = false;
225 self
226 }
227
228 pub fn default_values<I, S>(mut self, values: I) -> Self
230 where
231 I: IntoIterator<Item = S>,
232 S: Into<String>,
233 {
234 self.inner
235 .default
236 .extend(values.into_iter().map(Into::into));
237 if !self.inner.default.is_empty() {
238 self.inner.required = false;
239 }
240 self
241 }
242
243 pub fn help(mut self, text: impl Into<String>) -> Self {
245 self.inner.help = Some(text.into());
246 self
247 }
248
249 pub fn help_long(mut self, text: impl Into<String>) -> Self {
251 self.inner.help_long = Some(text.into());
252 self
253 }
254
255 pub fn help_md(mut self, text: impl Into<String>) -> Self {
257 self.inner.help_md = Some(text.into());
258 self
259 }
260
261 pub fn var(mut self, is_var: bool) -> Self {
263 self.inner.var = is_var;
264 self
265 }
266
267 pub fn var_min(mut self, min: usize) -> Self {
269 self.inner.var_min = Some(min);
270 self
271 }
272
273 pub fn var_max(mut self, max: usize) -> Self {
275 self.inner.var_max = Some(max);
276 self
277 }
278
279 pub fn required(mut self, is_required: bool) -> Self {
281 self.inner.required = is_required;
282 self
283 }
284
285 pub fn hide(mut self, is_hidden: bool) -> Self {
287 self.inner.hide = is_hidden;
288 self
289 }
290
291 pub fn env(mut self, env: impl Into<String>) -> Self {
293 self.inner.env = Some(env.into());
294 self
295 }
296
297 pub fn double_dash(mut self, behavior: SpecDoubleDashChoices) -> Self {
299 self.inner.double_dash = behavior;
300 self
301 }
302
303 #[must_use]
305 pub fn build(mut self) -> SpecArg {
306 self.inner.usage = self.inner.usage();
307 self.inner
308 }
309}
310
311#[derive(Debug, Default, Clone)]
313pub struct SpecCommandBuilder {
314 inner: SpecCommand,
315}
316
317impl SpecCommandBuilder {
318 pub fn new() -> Self {
320 Self::default()
321 }
322
323 pub fn name(mut self, name: impl Into<String>) -> Self {
325 self.inner.name = name.into();
326 self
327 }
328
329 pub fn alias(mut self, alias: impl Into<String>) -> Self {
331 self.inner.aliases.push(alias.into());
332 self
333 }
334
335 pub fn aliases<I, S>(mut self, aliases: I) -> Self
337 where
338 I: IntoIterator<Item = S>,
339 S: Into<String>,
340 {
341 self.inner
342 .aliases
343 .extend(aliases.into_iter().map(Into::into));
344 self
345 }
346
347 pub fn hidden_alias(mut self, alias: impl Into<String>) -> Self {
349 self.inner.hidden_aliases.push(alias.into());
350 self
351 }
352
353 pub fn hidden_aliases<I, S>(mut self, aliases: I) -> Self
355 where
356 I: IntoIterator<Item = S>,
357 S: Into<String>,
358 {
359 self.inner
360 .hidden_aliases
361 .extend(aliases.into_iter().map(Into::into));
362 self
363 }
364
365 pub fn flag(mut self, flag: SpecFlag) -> Self {
367 self.inner.flags.push(flag);
368 self
369 }
370
371 pub fn flags(mut self, flags: impl IntoIterator<Item = SpecFlag>) -> Self {
373 self.inner.flags.extend(flags);
374 self
375 }
376
377 pub fn arg(mut self, arg: SpecArg) -> Self {
379 self.inner.args.push(arg);
380 self
381 }
382
383 pub fn args(mut self, args: impl IntoIterator<Item = SpecArg>) -> Self {
385 self.inner.args.extend(args);
386 self
387 }
388
389 pub fn help(mut self, text: impl Into<String>) -> Self {
391 self.inner.help = Some(text.into());
392 self
393 }
394
395 pub fn help_long(mut self, text: impl Into<String>) -> Self {
397 self.inner.help_long = Some(text.into());
398 self
399 }
400
401 pub fn help_md(mut self, text: impl Into<String>) -> Self {
403 self.inner.help_md = Some(text.into());
404 self
405 }
406
407 pub fn hide(mut self, is_hidden: bool) -> Self {
409 self.inner.hide = is_hidden;
410 self
411 }
412
413 pub fn subcommand_required(mut self, required: bool) -> Self {
415 self.inner.subcommand_required = required;
416 self
417 }
418
419 pub fn deprecated(mut self, msg: impl Into<String>) -> Self {
421 self.inner.deprecated = Some(msg.into());
422 self
423 }
424
425 pub fn restart_token(mut self, token: impl Into<String>) -> Self {
428 self.inner.restart_token = Some(token.into());
429 self
430 }
431
432 #[must_use]
434 pub fn build(mut self) -> SpecCommand {
435 self.inner.usage = self.inner.usage();
436 self.inner
437 }
438}
439
440#[cfg(test)]
441mod tests {
442 use super::*;
443
444 #[test]
445 fn test_flag_builder_basic() {
446 let flag = SpecFlagBuilder::new()
447 .name("verbose")
448 .short('v')
449 .long("verbose")
450 .help("Enable verbose output")
451 .build();
452
453 assert_eq!(flag.name, "verbose");
454 assert_eq!(flag.short, vec!['v']);
455 assert_eq!(flag.long, vec!["verbose".to_string()]);
456 assert_eq!(flag.help, Some("Enable verbose output".to_string()));
457 }
458
459 #[test]
460 fn test_flag_builder_multiple_values() {
461 let flag = SpecFlagBuilder::new()
462 .shorts(['v', 'V'])
463 .longs(["verbose", "loud"])
464 .default_values(["info", "warn"])
465 .build();
466
467 assert_eq!(flag.short, vec!['v', 'V']);
468 assert_eq!(flag.long, vec!["verbose".to_string(), "loud".to_string()]);
469 assert_eq!(flag.default, vec!["info".to_string(), "warn".to_string()]);
470 assert!(!flag.required); }
472
473 #[test]
474 fn test_flag_builder_variadic() {
475 let flag = SpecFlagBuilder::new()
476 .long("file")
477 .var(true)
478 .var_min(1)
479 .var_max(10)
480 .build();
481
482 assert!(flag.var);
483 assert_eq!(flag.var_min, Some(1));
484 assert_eq!(flag.var_max, Some(10));
485 }
486
487 #[test]
488 fn test_flag_builder_name_derivation() {
489 let flag = SpecFlagBuilder::new().short('v').long("verbose").build();
490
491 assert_eq!(flag.name, "verbose");
493
494 let flag2 = SpecFlagBuilder::new().short('v').build();
495
496 assert_eq!(flag2.name, "v");
498 }
499
500 #[test]
501 fn test_arg_builder_basic() {
502 let arg = SpecArgBuilder::new()
503 .name("file")
504 .help("Input file")
505 .required(true)
506 .build();
507
508 assert_eq!(arg.name, "file");
509 assert_eq!(arg.help, Some("Input file".to_string()));
510 assert!(arg.required);
511 }
512
513 #[test]
514 fn test_arg_builder_variadic() {
515 let arg = SpecArgBuilder::new()
516 .name("files")
517 .var(true)
518 .var_min(1)
519 .var_max(10)
520 .help("Input files")
521 .build();
522
523 assert_eq!(arg.name, "files");
524 assert!(arg.var);
525 assert_eq!(arg.var_min, Some(1));
526 assert_eq!(arg.var_max, Some(10));
527 }
528
529 #[test]
530 fn test_arg_builder_defaults() {
531 let arg = SpecArgBuilder::new()
532 .name("file")
533 .default_values(["a.txt", "b.txt"])
534 .build();
535
536 assert_eq!(arg.default, vec!["a.txt".to_string(), "b.txt".to_string()]);
537 assert!(!arg.required);
538 }
539
540 #[test]
541 fn test_command_builder_basic() {
542 let cmd = SpecCommandBuilder::new()
543 .name("install")
544 .help("Install packages")
545 .build();
546
547 assert_eq!(cmd.name, "install");
548 assert_eq!(cmd.help, Some("Install packages".to_string()));
549 }
550
551 #[test]
552 fn test_command_builder_aliases() {
553 let cmd = SpecCommandBuilder::new()
554 .name("install")
555 .alias("i")
556 .aliases(["add", "get"])
557 .hidden_aliases(["inst"])
558 .build();
559
560 assert_eq!(
561 cmd.aliases,
562 vec!["i".to_string(), "add".to_string(), "get".to_string()]
563 );
564 assert_eq!(cmd.hidden_aliases, vec!["inst".to_string()]);
565 }
566
567 #[test]
568 fn test_command_builder_with_flags_and_args() {
569 let flag = SpecFlagBuilder::new().short('f').long("force").build();
570
571 let arg = SpecArgBuilder::new().name("package").required(true).build();
572
573 let cmd = SpecCommandBuilder::new()
574 .name("install")
575 .flag(flag)
576 .arg(arg)
577 .build();
578
579 assert_eq!(cmd.flags.len(), 1);
580 assert_eq!(cmd.flags[0].name, "force");
581 assert_eq!(cmd.args.len(), 1);
582 assert_eq!(cmd.args[0].name, "package");
583 }
584}