1use crate::spec::cmd::SpecExample;
34use crate::spec::effect::SpecCommandEffect;
35use crate::{
36 spec::arg::SpecDoubleDashChoices, SpecArg, SpecChoices, SpecCommand, SpecDefaultIf, SpecFlag,
37 SpecRequiredIfEq, SpecRequiresIf,
38};
39
40#[derive(Debug, Default, Clone)]
42pub struct SpecFlagBuilder {
43 inner: SpecFlag,
44 allow_hyphen_values: bool,
45}
46
47impl SpecFlagBuilder {
48 pub fn new() -> Self {
50 Self::default()
51 }
52
53 pub fn name(mut self, name: impl Into<String>) -> Self {
55 self.inner.name = name.into();
56 self
57 }
58
59 pub fn short(mut self, c: char) -> Self {
61 self.inner.short.push(c);
62 self
63 }
64
65 pub fn shorts(mut self, chars: impl IntoIterator<Item = char>) -> Self {
67 self.inner.short.extend(chars);
68 self
69 }
70
71 pub fn long(mut self, name: impl Into<String>) -> Self {
73 self.inner.long.push(name.into());
74 self
75 }
76
77 pub fn longs<I, S>(mut self, names: I) -> Self
79 where
80 I: IntoIterator<Item = S>,
81 S: Into<String>,
82 {
83 self.inner.long.extend(names.into_iter().map(Into::into));
84 self
85 }
86
87 pub fn default_value(mut self, value: impl Into<String>) -> Self {
89 self.inner.default.push(value.into());
90 self.inner.required = false;
91 self
92 }
93
94 pub fn default_values<I, S>(mut self, values: I) -> Self
96 where
97 I: IntoIterator<Item = S>,
98 S: Into<String>,
99 {
100 self.inner
101 .default
102 .extend(values.into_iter().map(Into::into));
103 if !self.inner.default.is_empty() {
104 self.inner.required = false;
105 }
106 self
107 }
108
109 pub fn help(mut self, text: impl Into<String>) -> Self {
111 self.inner.help = Some(text.into());
112 self
113 }
114
115 pub fn help_long(mut self, text: impl Into<String>) -> Self {
117 self.inner.help_long = Some(text.into());
118 self
119 }
120
121 pub fn help_md(mut self, text: impl Into<String>) -> Self {
123 self.inner.help_md = Some(text.into());
124 self
125 }
126
127 pub fn var(mut self, is_var: bool) -> Self {
129 self.inner.var = is_var;
130 self
131 }
132
133 pub fn var_min(mut self, min: usize) -> Self {
135 self.inner.var_min = Some(min);
136 self
137 }
138
139 pub fn var_max(mut self, max: usize) -> Self {
141 self.inner.var_max = Some(max);
142 self
143 }
144
145 pub fn required(mut self, is_required: bool) -> Self {
147 self.inner.required = is_required;
148 self
149 }
150
151 pub fn required_if(mut self, flag: impl Into<String>) -> Self {
153 self.inner.required_if.push(flag.into());
154 self
155 }
156
157 pub fn required_if_any<I, S>(mut self, flags: I) -> Self
159 where
160 I: IntoIterator<Item = S>,
161 S: Into<String>,
162 {
163 self.inner
164 .required_if
165 .extend(flags.into_iter().map(Into::into));
166 self
167 }
168
169 pub fn required_if_eq(mut self, selector: impl Into<String>, value: impl Into<String>) -> Self {
171 self.inner.required_if_eq.push(SpecRequiredIfEq {
172 selector: selector.into(),
173 value: value.into(),
174 });
175 self
176 }
177
178 pub fn required_if_eq_all<I, S, V>(mut self, conditions: I) -> Self
180 where
181 I: IntoIterator<Item = (S, V)>,
182 S: Into<String>,
183 V: Into<String>,
184 {
185 self.inner
186 .required_if_eq_all
187 .extend(
188 conditions
189 .into_iter()
190 .map(|(selector, value)| SpecRequiredIfEq {
191 selector: selector.into(),
192 value: value.into(),
193 }),
194 );
195 self
196 }
197
198 pub fn required_unless(mut self, flag: impl Into<String>) -> Self {
200 self.inner.required_unless.push(flag.into());
201 self
202 }
203
204 pub fn required_unless_any<I, S>(mut self, flags: I) -> Self
206 where
207 I: IntoIterator<Item = S>,
208 S: Into<String>,
209 {
210 self.inner
211 .required_unless
212 .extend(flags.into_iter().map(Into::into));
213 self
214 }
215
216 pub fn required_unless_all<I, S>(mut self, flags: I) -> Self
218 where
219 I: IntoIterator<Item = S>,
220 S: Into<String>,
221 {
222 self.inner
223 .required_unless_all
224 .extend(flags.into_iter().map(Into::into));
225 self
226 }
227
228 pub fn global(mut self, is_global: bool) -> Self {
230 self.inner.global = is_global;
231 self
232 }
233
234 pub fn hide(mut self, is_hidden: bool) -> Self {
236 self.inner.hide = is_hidden;
237 self
238 }
239
240 pub fn count(mut self, is_count: bool) -> Self {
242 self.inner.count = is_count;
243 self
244 }
245
246 pub fn allow_hyphen_values(mut self, allow: bool) -> Self {
248 self.allow_hyphen_values = allow;
249 if let Some(arg) = &mut self.inner.arg {
250 arg.double_dash = if allow {
251 crate::spec::arg::SpecDoubleDashChoices::Automatic
252 } else {
253 crate::spec::arg::SpecDoubleDashChoices::Optional
254 };
255 }
256 self
257 }
258
259 pub fn require_equals(mut self, require: bool) -> Self {
261 self.inner.require_equals = require;
262 self
263 }
264
265 pub fn value_optional(mut self, optional: bool) -> Self {
267 self.inner.value_optional = optional;
268 self
269 }
270
271 pub fn bool_value(mut self, enabled: bool) -> Self {
273 self.inner.bool_value = enabled;
274 self
275 }
276
277 pub fn default_missing(mut self, value: impl Into<String>) -> Self {
279 self.inner.default_missing = Some(value.into());
280 self
281 }
282
283 pub fn arg(mut self, arg: SpecArg) -> Self {
285 self.inner.arg = Some(arg);
286 if self.allow_hyphen_values {
287 if let Some(arg) = &mut self.inner.arg {
288 arg.double_dash = crate::spec::arg::SpecDoubleDashChoices::Automatic;
289 }
290 }
291 self
292 }
293
294 pub fn negate(mut self, negate: impl Into<String>) -> Self {
296 self.inner.negate = Some(negate.into());
297 self
298 }
299
300 pub fn override_with(mut self, flag: impl Into<String>) -> Self {
302 self.inner.overrides.push(flag.into());
303 self
304 }
305
306 pub fn overrides_with<I, S>(mut self, flags: I) -> Self
308 where
309 I: IntoIterator<Item = S>,
310 S: Into<String>,
311 {
312 self.inner
313 .overrides
314 .extend(flags.into_iter().map(Into::into));
315 self
316 }
317
318 pub fn require(mut self, flag: impl Into<String>) -> Self {
320 self.inner.requires.push(flag.into());
321 self
322 }
323
324 pub fn requires<I, S>(mut self, flags: I) -> Self
326 where
327 I: IntoIterator<Item = S>,
328 S: Into<String>,
329 {
330 self.inner
331 .requires
332 .extend(flags.into_iter().map(Into::into));
333 self
334 }
335
336 pub fn requires_if(mut self, value: impl Into<String>, flag: impl Into<String>) -> Self {
338 self.inner.requires_if.push(SpecRequiresIf {
339 value: value.into(),
340 requires: flag.into(),
341 });
342 self
343 }
344
345 pub fn requires_ifs<I, V, S>(mut self, requirements: I) -> Self
347 where
348 I: IntoIterator<Item = (V, S)>,
349 V: Into<String>,
350 S: Into<String>,
351 {
352 self.inner
353 .requires_if
354 .extend(
355 requirements
356 .into_iter()
357 .map(|(value, requires)| SpecRequiresIf {
358 value: value.into(),
359 requires: requires.into(),
360 }),
361 );
362 self
363 }
364
365 pub fn default_if(mut self, selector: impl Into<String>, value: impl Into<String>) -> Self {
369 self.inner.default_if.push(SpecDefaultIf {
370 selector: selector.into(),
371 when: None,
372 value: value.into(),
373 });
374 self
375 }
376
377 pub fn default_if_eq(
381 mut self,
382 selector: impl Into<String>,
383 when: impl Into<String>,
384 value: impl Into<String>,
385 ) -> Self {
386 self.inner.default_if.push(SpecDefaultIf {
387 selector: selector.into(),
388 when: Some(when.into()),
389 value: value.into(),
390 });
391 self
392 }
393
394 pub fn default_ifs<I>(mut self, conditions: I) -> Self
396 where
397 I: IntoIterator<Item = SpecDefaultIf>,
398 {
399 self.inner.default_if.extend(conditions);
400 self
401 }
402
403 pub fn help_heading(mut self, help_heading: impl Into<String>) -> Self {
405 self.inner.help_heading = Some(help_heading.into());
406 self
407 }
408
409 pub fn action(mut self, action: crate::SpecFlagAction) -> Self {
411 self.inner.action = action;
412 self
413 }
414
415 pub fn env(mut self, env: impl Into<String>) -> Self {
416 self.inner.env = Some(env.into());
417 self
418 }
419
420 pub fn env_fallback(mut self, env: impl Into<String>) -> Self {
422 self.inner.env_fallback.push(env.into());
423 self
424 }
425
426 pub fn deprecated_env(mut self, env: impl Into<String>) -> Self {
428 self.inner.deprecated_env.push(env.into());
429 self
430 }
431
432 pub fn deprecated(mut self, msg: impl Into<String>) -> Self {
434 self.inner.deprecated = Some(msg.into());
435 self
436 }
437
438 pub fn deprecated_warn_at(mut self, version: impl Into<String>) -> Self {
439 self.inner.deprecated_warn_at = Some(version.into());
440 self
441 }
442
443 pub fn deprecated_remove_at(mut self, version: impl Into<String>) -> Self {
444 self.inner.deprecated_remove_at = Some(version.into());
445 self
446 }
447
448 pub fn usage(mut self, usage: impl Into<String>) -> Self {
450 self.inner.usage = usage.into();
451 self
452 }
453
454 pub fn help_first_line(mut self, text: impl Into<String>) -> Self {
456 self.inner.help_first_line = Some(text.into());
457 self
458 }
459
460 pub fn effect(mut self, effect: SpecCommandEffect) -> Self {
462 self.inner.effect = Some(effect);
463 self
464 }
465
466 #[must_use]
468 pub fn build(mut self) -> SpecFlag {
469 if self.allow_hyphen_values {
470 if let Some(arg) = &mut self.inner.arg {
471 arg.double_dash = crate::spec::arg::SpecDoubleDashChoices::Automatic;
472 }
473 }
474 if self.inner.default_missing.is_some() {
475 if let Some(arg) = &mut self.inner.arg {
476 arg.required = false;
477 }
478 }
479 self.inner.usage = self.inner.usage();
480 if self.inner.name.is_empty() {
481 if let Some(long) = self.inner.long.first() {
483 self.inner.name = long.clone();
484 } else if let Some(short) = self.inner.short.first() {
485 self.inner.name = short.to_string();
486 }
487 }
488 self.inner
489 }
490}
491
492#[derive(Debug, Default, Clone)]
494pub struct SpecArgBuilder {
495 inner: SpecArg,
496}
497
498impl SpecArgBuilder {
499 pub fn new() -> Self {
501 Self::default()
502 }
503
504 pub fn name(mut self, name: impl Into<String>) -> Self {
506 self.inner.name = name.into();
507 self
508 }
509
510 pub fn value_names<I, S>(mut self, names: I) -> Self
512 where
513 I: IntoIterator<Item = S>,
514 S: Into<String>,
515 {
516 self.inner.value_names = names.into_iter().map(Into::into).collect();
517 if let Some(first) = self.inner.value_names.first() {
518 self.inner.name.clone_from(first);
519 }
520 if self.inner.value_names.len() > 1 {
521 let arity = self.inner.value_names.len();
522 self.inner.var = true;
523 self.inner.var_min = Some(arity);
524 self.inner.var_max = Some(arity);
525 }
526 self
527 }
528
529 pub fn default_value(mut self, value: impl Into<String>) -> Self {
531 self.inner.default.push(value.into());
532 self.inner.required = false;
533 self
534 }
535
536 pub fn default_values<I, S>(mut self, values: I) -> Self
538 where
539 I: IntoIterator<Item = S>,
540 S: Into<String>,
541 {
542 self.inner
543 .default
544 .extend(values.into_iter().map(Into::into));
545 if !self.inner.default.is_empty() {
546 self.inner.required = false;
547 }
548 self
549 }
550
551 pub fn help(mut self, text: impl Into<String>) -> Self {
553 self.inner.help = Some(text.into());
554 self
555 }
556
557 pub fn help_long(mut self, text: impl Into<String>) -> Self {
559 self.inner.help_long = Some(text.into());
560 self
561 }
562
563 pub fn help_md(mut self, text: impl Into<String>) -> Self {
565 self.inner.help_md = Some(text.into());
566 self
567 }
568
569 pub fn var(mut self, is_var: bool) -> Self {
571 self.inner.var = is_var;
572 self
573 }
574
575 pub fn var_min(mut self, min: usize) -> Self {
577 self.inner.var_min = Some(min);
578 self
579 }
580
581 pub fn var_max(mut self, max: usize) -> Self {
583 self.inner.var_max = Some(max);
584 self
585 }
586
587 pub fn required(mut self, is_required: bool) -> Self {
589 self.inner.required = is_required;
590 self
591 }
592
593 pub fn requires<I, S>(mut self, selectors: I) -> Self
595 where
596 I: IntoIterator<Item = S>,
597 S: Into<String>,
598 {
599 self.inner
600 .requires
601 .extend(selectors.into_iter().map(Into::into));
602 self
603 }
604
605 pub fn required_if_any<I, S>(mut self, selectors: I) -> Self
607 where
608 I: IntoIterator<Item = S>,
609 S: Into<String>,
610 {
611 self.inner
612 .required_if
613 .extend(selectors.into_iter().map(Into::into));
614 self
615 }
616
617 pub fn required_if_eq(mut self, selector: impl Into<String>, value: impl Into<String>) -> Self {
619 self.inner.required_if_eq.push(SpecRequiredIfEq {
620 selector: selector.into(),
621 value: value.into(),
622 });
623 self
624 }
625
626 pub fn required_if_eq_all<I, S, V>(mut self, conditions: I) -> Self
628 where
629 I: IntoIterator<Item = (S, V)>,
630 S: Into<String>,
631 V: Into<String>,
632 {
633 self.inner
634 .required_if_eq_all
635 .extend(
636 conditions
637 .into_iter()
638 .map(|(selector, value)| SpecRequiredIfEq {
639 selector: selector.into(),
640 value: value.into(),
641 }),
642 );
643 self
644 }
645
646 pub fn required_unless_any<I, S>(mut self, selectors: I) -> Self
648 where
649 I: IntoIterator<Item = S>,
650 S: Into<String>,
651 {
652 self.inner
653 .required_unless
654 .extend(selectors.into_iter().map(Into::into));
655 self
656 }
657
658 pub fn required_unless_all<I, S>(mut self, selectors: I) -> Self
660 where
661 I: IntoIterator<Item = S>,
662 S: Into<String>,
663 {
664 self.inner
665 .required_unless_all
666 .extend(selectors.into_iter().map(Into::into));
667 self
668 }
669
670 pub fn hide(mut self, is_hidden: bool) -> Self {
672 self.inner.hide = is_hidden;
673 self
674 }
675
676 pub fn help_heading(mut self, help_heading: impl Into<String>) -> Self {
679 self.inner.help_heading = Some(help_heading.into());
680 self
681 }
682
683 pub fn env(mut self, env: impl Into<String>) -> Self {
684 self.inner.env = Some(env.into());
685 self
686 }
687
688 pub fn env_fallback(mut self, env: impl Into<String>) -> Self {
690 self.inner.env_fallback.push(env.into());
691 self
692 }
693
694 pub fn deprecated_env(mut self, env: impl Into<String>) -> Self {
696 self.inner.deprecated_env.push(env.into());
697 self
698 }
699
700 pub fn double_dash(mut self, behavior: SpecDoubleDashChoices) -> Self {
702 self.inner.double_dash = behavior;
703 self
704 }
705
706 pub fn choices<I, S>(mut self, choices: I) -> Self
708 where
709 I: IntoIterator<Item = S>,
710 S: Into<String>,
711 {
712 let spec_choices = self.inner.choices.get_or_insert_with(SpecChoices::default);
713 #[cfg(feature = "unstable_choices_env")]
714 let env = spec_choices.env().map(ToString::to_string);
715 spec_choices.choices = choices.into_iter().map(Into::into).collect();
716 #[cfg(feature = "unstable_choices_env")]
717 spec_choices.set_env(env);
718 self
719 }
720
721 pub fn validate(mut self, expression: impl Into<String>) -> Self {
723 self.inner.validate = Some(expression.into());
724 self
725 }
726
727 pub fn validate_error(mut self, message: impl Into<String>) -> Self {
729 self.inner.validate_error = Some(message.into());
730 self
731 }
732
733 #[cfg(feature = "unstable_choices_env")]
735 pub fn choices_env(mut self, env: impl Into<String>) -> Self {
736 let choices = self.inner.choices.get_or_insert_with(SpecChoices::default);
737 choices.set_env(Some(env.into()));
738 self
739 }
740
741 pub fn usage(mut self, usage: impl Into<String>) -> Self {
743 self.inner.usage = usage.into();
744 self
745 }
746
747 pub fn help_first_line(mut self, text: impl Into<String>) -> Self {
749 self.inner.help_first_line = Some(text.into());
750 self
751 }
752
753 pub fn effect(mut self, effect: SpecCommandEffect) -> Self {
755 self.inner.effect = Some(effect);
756 self
757 }
758
759 #[must_use]
761 pub fn build(mut self) -> SpecArg {
762 if self.inner.validate.is_none() {
763 self.inner.validate_error = None;
764 }
765 if self.inner.value_names.len() > 1 {
766 let arity = self.inner.value_names.len();
767 self.inner.var = true;
768 self.inner.var_min = Some(arity);
769 self.inner.var_max = Some(arity);
770 }
771 self.inner.usage = self.inner.usage();
772 self.inner
773 }
774}
775
776#[derive(Debug, Default, Clone)]
778pub struct SpecCommandBuilder {
779 inner: SpecCommand,
780}
781
782impl SpecCommandBuilder {
783 pub fn new() -> Self {
785 Self::default()
786 }
787
788 pub fn name(mut self, name: impl Into<String>) -> Self {
790 self.inner.name = name.into();
791 self
792 }
793
794 pub fn alias(mut self, alias: impl Into<String>) -> Self {
796 self.inner.aliases.push(alias.into());
797 self
798 }
799
800 pub fn aliases<I, S>(mut self, aliases: I) -> Self
802 where
803 I: IntoIterator<Item = S>,
804 S: Into<String>,
805 {
806 self.inner
807 .aliases
808 .extend(aliases.into_iter().map(Into::into));
809 self
810 }
811
812 pub fn hidden_alias(mut self, alias: impl Into<String>) -> Self {
814 self.inner.hidden_aliases.push(alias.into());
815 self
816 }
817
818 pub fn hidden_aliases<I, S>(mut self, aliases: I) -> Self
820 where
821 I: IntoIterator<Item = S>,
822 S: Into<String>,
823 {
824 self.inner
825 .hidden_aliases
826 .extend(aliases.into_iter().map(Into::into));
827 self
828 }
829
830 pub fn flag(mut self, flag: SpecFlag) -> Self {
832 self.inner.flags.push(flag);
833 self
834 }
835
836 pub fn flags(mut self, flags: impl IntoIterator<Item = SpecFlag>) -> Self {
838 self.inner.flags.extend(flags);
839 self
840 }
841
842 pub fn arg(mut self, arg: SpecArg) -> Self {
844 self.inner.args.push(arg);
845 self
846 }
847
848 pub fn args(mut self, args: impl IntoIterator<Item = SpecArg>) -> Self {
850 self.inner.args.extend(args);
851 self
852 }
853
854 pub fn help(mut self, text: impl Into<String>) -> Self {
856 self.inner.help = Some(text.into());
857 self
858 }
859
860 pub fn help_long(mut self, text: impl Into<String>) -> Self {
862 self.inner.help_long = Some(text.into());
863 self
864 }
865
866 pub fn help_md(mut self, text: impl Into<String>) -> Self {
868 self.inner.help_md = Some(text.into());
869 self
870 }
871
872 pub fn hide(mut self, is_hidden: bool) -> Self {
874 self.inner.hide = is_hidden;
875 self
876 }
877
878 pub fn subcommand_required(mut self, required: bool) -> Self {
880 self.inner.subcommand_required = required;
881 self
882 }
883
884 pub fn subcommand_help_heading(mut self, heading: impl Into<String>) -> Self {
886 self.inner.subcommand_help_heading = Some(heading.into());
887 self
888 }
889
890 pub fn subcommand_value_name(mut self, name: impl Into<String>) -> Self {
892 self.inner.subcommand_value_name = Some(name.into());
893 self
894 }
895
896 pub fn term_width(mut self, width: usize) -> Self {
898 self.inner.term_width = Some(width);
899 self
900 }
901
902 pub fn max_term_width(mut self, width: usize) -> Self {
904 self.inner.max_term_width = Some(width);
905 self
906 }
907
908 pub fn external_subcommand(mut self, enabled: bool) -> Self {
910 self.inner.external_subcommand = enabled;
911 self
912 }
913
914 pub fn disable_help_flag(mut self, disabled: bool) -> Self {
916 self.inner.disable_help_flag = disabled;
917 self
918 }
919
920 pub fn disable_help_subcommand(mut self, disabled: bool) -> Self {
922 self.inner.disable_help_subcommand = disabled;
923 self
924 }
925
926 pub fn disable_version_flag(mut self, disabled: bool) -> Self {
928 self.inner.disable_version_flag = disabled;
929 self
930 }
931
932 pub fn args_override_self(mut self, enabled: bool) -> Self {
936 self.inner.args_override_self = enabled;
937 self
938 }
939
940 pub fn subcommand_negates_reqs(mut self, enabled: bool) -> Self {
942 self.inner.subcommand_negates_reqs = enabled;
943 self
944 }
945
946 pub fn args_conflicts_with_subcommands(mut self, enabled: bool) -> Self {
948 self.inner.args_conflicts_with_subcommands = enabled;
949 self
950 }
951
952 pub fn subcommand_precedence_over_arg(mut self, enabled: bool) -> Self {
953 self.inner.subcommand_precedence_over_arg = enabled;
954 self
955 }
956
957 pub fn allow_missing_positional(mut self, enabled: bool) -> Self {
958 self.inner.allow_missing_positional = enabled;
959 self
960 }
961
962 pub fn effect(mut self, effect: SpecCommandEffect) -> Self {
964 self.inner.effect = Some(effect);
965 self
966 }
967
968 pub fn deprecated(mut self, msg: impl Into<String>) -> Self {
970 self.inner.deprecated = Some(msg.into());
971 self
972 }
973
974 pub fn deprecated_warn_at(mut self, version: impl Into<String>) -> Self {
975 self.inner.deprecated_warn_at = Some(version.into());
976 self
977 }
978
979 pub fn deprecated_remove_at(mut self, version: impl Into<String>) -> Self {
980 self.inner.deprecated_remove_at = Some(version.into());
981 self
982 }
983
984 pub fn restart_token(mut self, token: impl Into<String>) -> Self {
987 self.inner.restart_token = Some(token.into());
988 self
989 }
990
991 pub fn subcommand(mut self, cmd: SpecCommand) -> Self {
993 self.inner.subcommands.insert(cmd.name.clone(), cmd);
994 self
995 }
996
997 pub fn subcommands(mut self, cmds: impl IntoIterator<Item = SpecCommand>) -> Self {
999 for cmd in cmds {
1000 self.inner.subcommands.insert(cmd.name.clone(), cmd);
1001 }
1002 self
1003 }
1004
1005 pub fn before_help(mut self, text: impl Into<String>) -> Self {
1007 self.inner.before_help = Some(text.into());
1008 self
1009 }
1010
1011 pub fn before_help_long(mut self, text: impl Into<String>) -> Self {
1013 self.inner.before_help_long = Some(text.into());
1014 self
1015 }
1016
1017 pub fn before_help_md(mut self, text: impl Into<String>) -> Self {
1019 self.inner.before_help_md = Some(text.into());
1020 self
1021 }
1022
1023 pub fn after_help(mut self, text: impl Into<String>) -> Self {
1025 self.inner.after_help = Some(text.into());
1026 self
1027 }
1028
1029 pub fn after_help_long(mut self, text: impl Into<String>) -> Self {
1031 self.inner.after_help_long = Some(text.into());
1032 self
1033 }
1034
1035 pub fn after_help_md(mut self, text: impl Into<String>) -> Self {
1037 self.inner.after_help_md = Some(text.into());
1038 self
1039 }
1040
1041 pub fn example(mut self, code: impl Into<String>) -> Self {
1043 self.inner.examples.push(SpecExample::new(code.into()));
1044 self
1045 }
1046
1047 pub fn example_with_help(
1049 mut self,
1050 code: impl Into<String>,
1051 header: impl Into<String>,
1052 help: impl Into<String>,
1053 ) -> Self {
1054 let mut example = SpecExample::new(code.into());
1055 example.header = Some(header.into());
1056 example.help = Some(help.into());
1057 self.inner.examples.push(example);
1058 self
1059 }
1060
1061 #[must_use]
1063 pub fn build(mut self) -> SpecCommand {
1064 self.inner.usage = self.inner.usage();
1065 self.inner
1066 }
1067}
1068
1069#[cfg(test)]
1070mod tests {
1071 use super::*;
1072
1073 #[test]
1074 fn test_flag_builder_basic() {
1075 let flag = SpecFlagBuilder::new()
1076 .name("verbose")
1077 .short('v')
1078 .long("verbose")
1079 .help("Enable verbose output")
1080 .build();
1081
1082 assert_eq!(flag.name, "verbose");
1083 assert_eq!(flag.short, vec!['v']);
1084 assert_eq!(flag.long, vec!["verbose".to_string()]);
1085 assert_eq!(flag.help, Some("Enable verbose output".to_string()));
1086 }
1087
1088 #[test]
1089 fn test_flag_builder_multiple_values() {
1090 let flag = SpecFlagBuilder::new()
1091 .shorts(['v', 'V'])
1092 .longs(["verbose", "loud"])
1093 .default_values(["info", "warn"])
1094 .build();
1095
1096 assert_eq!(flag.short, vec!['v', 'V']);
1097 assert_eq!(flag.long, vec!["verbose".to_string(), "loud".to_string()]);
1098 assert_eq!(flag.default, vec!["info".to_string(), "warn".to_string()]);
1099 assert!(!flag.required); }
1101
1102 #[test]
1103 fn test_flag_builder_variadic() {
1104 let flag = SpecFlagBuilder::new()
1105 .long("file")
1106 .var(true)
1107 .var_min(1)
1108 .var_max(10)
1109 .build();
1110
1111 assert!(flag.var);
1112 assert_eq!(flag.var_min, Some(1));
1113 assert_eq!(flag.var_max, Some(10));
1114 }
1115
1116 #[test]
1117 fn test_flag_builder_conditional_requirements() {
1118 let flag = SpecFlagBuilder::new()
1119 .long("config")
1120 .requires_if("special.toml", "--key")
1121 .requires_ifs([("remote.toml", "--token"), ("signed.toml", "--identity")])
1122 .build();
1123
1124 assert_eq!(
1125 flag.requires_if,
1126 [
1127 SpecRequiresIf {
1128 value: "special.toml".into(),
1129 requires: "--key".into(),
1130 },
1131 SpecRequiresIf {
1132 value: "remote.toml".into(),
1133 requires: "--token".into(),
1134 },
1135 SpecRequiresIf {
1136 value: "signed.toml".into(),
1137 requires: "--identity".into(),
1138 },
1139 ]
1140 );
1141 }
1142
1143 #[test]
1144 fn test_flag_builder_conditional_defaults() {
1145 let flag = SpecFlagBuilder::new()
1146 .long("bin-names")
1147 .default_if("--json", "true")
1148 .default_if_eq("--output", "json", "pretty")
1149 .build();
1150
1151 assert_eq!(
1152 flag.default_if,
1153 [
1154 SpecDefaultIf {
1155 selector: "--json".into(),
1156 when: None,
1157 value: "true".into(),
1158 },
1159 SpecDefaultIf {
1160 selector: "--output".into(),
1161 when: Some("json".into()),
1162 value: "pretty".into(),
1163 },
1164 ]
1165 );
1166 }
1167
1168 #[test]
1169 fn test_flag_builder_name_derivation() {
1170 let flag = SpecFlagBuilder::new().short('v').long("verbose").build();
1171
1172 assert_eq!(flag.name, "verbose");
1174
1175 let flag2 = SpecFlagBuilder::new().short('v').build();
1176
1177 assert_eq!(flag2.name, "v");
1179 }
1180
1181 #[test]
1182 fn test_arg_builder_basic() {
1183 let arg = SpecArgBuilder::new()
1184 .name("file")
1185 .help("Input file")
1186 .required(true)
1187 .build();
1188
1189 assert_eq!(arg.name, "file");
1190 assert_eq!(arg.help, Some("Input file".to_string()));
1191 assert!(arg.required);
1192 }
1193
1194 #[test]
1195 fn test_arg_builder_variadic() {
1196 let arg = SpecArgBuilder::new()
1197 .name("files")
1198 .var(true)
1199 .var_min(1)
1200 .var_max(10)
1201 .help("Input files")
1202 .build();
1203
1204 assert_eq!(arg.name, "files");
1205 assert!(arg.var);
1206 assert_eq!(arg.var_min, Some(1));
1207 assert_eq!(arg.var_max, Some(10));
1208 }
1209
1210 #[test]
1211 fn test_arg_builder_defaults() {
1212 let arg = SpecArgBuilder::new()
1213 .name("file")
1214 .default_values(["a.txt", "b.txt"])
1215 .build();
1216
1217 assert_eq!(arg.default, vec!["a.txt".to_string(), "b.txt".to_string()]);
1218 assert!(!arg.required);
1219 }
1220
1221 #[test]
1222 fn test_arg_builder_drops_validation_error_without_expression() {
1223 let arg = SpecArgBuilder::new()
1224 .name("port")
1225 .validate_error("must be a valid port")
1226 .build();
1227
1228 assert!(arg.validate_error.is_none());
1229 }
1230
1231 #[test]
1232 fn test_command_builder_basic() {
1233 let cmd = SpecCommandBuilder::new()
1234 .name("install")
1235 .help("Install packages")
1236 .build();
1237
1238 assert_eq!(cmd.name, "install");
1239 assert_eq!(cmd.help, Some("Install packages".to_string()));
1240 }
1241
1242 #[test]
1243 fn test_command_builder_aliases() {
1244 let cmd = SpecCommandBuilder::new()
1245 .name("install")
1246 .alias("i")
1247 .aliases(["add", "get"])
1248 .hidden_aliases(["inst"])
1249 .build();
1250
1251 assert_eq!(
1252 cmd.aliases,
1253 vec!["i".to_string(), "add".to_string(), "get".to_string()]
1254 );
1255 assert_eq!(cmd.hidden_aliases, vec!["inst".to_string()]);
1256 }
1257
1258 #[test]
1259 fn test_command_builder_with_flags_and_args() {
1260 let flag = SpecFlagBuilder::new().short('f').long("force").build();
1261
1262 let arg = SpecArgBuilder::new().name("package").required(true).build();
1263
1264 let cmd = SpecCommandBuilder::new()
1265 .name("install")
1266 .flag(flag)
1267 .arg(arg)
1268 .build();
1269
1270 assert_eq!(cmd.flags.len(), 1);
1271 assert_eq!(cmd.flags[0].name, "force");
1272 assert_eq!(cmd.args.len(), 1);
1273 assert_eq!(cmd.args[0].name, "package");
1274 }
1275
1276 #[test]
1277 fn test_arg_builder_choices() {
1278 let arg = SpecArgBuilder::new()
1279 .name("format")
1280 .choices(["json", "yaml", "toml"])
1281 .build();
1282
1283 assert!(arg.choices.is_some());
1284 let choices = arg.choices.unwrap();
1285 assert_eq!(
1286 choices.choices,
1287 vec!["json".to_string(), "yaml".to_string(), "toml".to_string()]
1288 );
1289 assert_eq!(choices.env(), None);
1290 }
1291
1292 #[cfg(feature = "unstable_choices_env")]
1293 #[test]
1294 fn test_arg_builder_choices_env() {
1295 let arg = SpecArgBuilder::new()
1296 .name("env")
1297 .choices(["local"])
1298 .choices_env("DEPLOY_ENVS")
1299 .build();
1300
1301 let choices = arg.choices.unwrap();
1302 assert_eq!(choices.choices, vec!["local".to_string()]);
1303 assert_eq!(choices.env(), Some("DEPLOY_ENVS"));
1304 }
1305
1306 #[cfg(feature = "unstable_choices_env")]
1307 #[test]
1308 fn test_arg_builder_choices_preserves_choices_env() {
1309 let arg = SpecArgBuilder::new()
1310 .name("env")
1311 .choices_env("DEPLOY_ENVS")
1312 .choices(["local"])
1313 .build();
1314
1315 let choices = arg.choices.unwrap();
1316 assert_eq!(choices.choices, vec!["local".to_string()]);
1317 assert_eq!(choices.env(), Some("DEPLOY_ENVS"));
1318 }
1319
1320 #[test]
1321 fn test_command_builder_subcommands() {
1322 let sub1 = SpecCommandBuilder::new().name("sub1").build();
1323 let sub2 = SpecCommandBuilder::new().name("sub2").build();
1324
1325 let cmd = SpecCommandBuilder::new()
1326 .name("main")
1327 .subcommand(sub1)
1328 .subcommand(sub2)
1329 .build();
1330
1331 assert_eq!(cmd.subcommands.len(), 2);
1332 assert!(cmd.subcommands.contains_key("sub1"));
1333 assert!(cmd.subcommands.contains_key("sub2"));
1334 }
1335
1336 #[test]
1337 fn test_command_builder_before_after_help() {
1338 let cmd = SpecCommandBuilder::new()
1339 .name("test")
1340 .before_help("Before help text")
1341 .before_help_long("Before help long text")
1342 .after_help("After help text")
1343 .after_help_long("After help long text")
1344 .build();
1345
1346 assert_eq!(cmd.before_help, Some("Before help text".to_string()));
1347 assert_eq!(
1348 cmd.before_help_long,
1349 Some("Before help long text".to_string())
1350 );
1351 assert_eq!(cmd.after_help, Some("After help text".to_string()));
1352 assert_eq!(
1353 cmd.after_help_long,
1354 Some("After help long text".to_string())
1355 );
1356 }
1357
1358 #[test]
1359 fn test_command_builder_examples() {
1360 let cmd = SpecCommandBuilder::new()
1361 .name("test")
1362 .example("mycli run")
1363 .example_with_help("mycli build", "Build example", "Build the project")
1364 .build();
1365
1366 assert_eq!(cmd.examples.len(), 2);
1367 assert_eq!(cmd.examples[0].code, "mycli run");
1368 assert_eq!(cmd.examples[1].code, "mycli build");
1369 assert_eq!(cmd.examples[1].header, Some("Build example".to_string()));
1370 assert_eq!(cmd.examples[1].help, Some("Build the project".to_string()));
1371 }
1372}