1use crate::spec::cmd::SpecExample;
34use crate::spec::effect::SpecCommandEffect;
35use crate::{
36 spec::arg::SpecDoubleDashChoices, SpecAdmonition, SpecArg, SpecChoices, SpecCommand,
37 SpecDefaultIf, SpecFlag, 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 note(mut self, text: impl Into<String>) -> Self {
129 self.inner.admonitions.push(SpecAdmonition::note(text));
130 self
131 }
132
133 pub fn warning(mut self, text: impl Into<String>) -> Self {
135 self.inner.admonitions.push(SpecAdmonition::warning(text));
136 self
137 }
138
139 pub fn surface(mut self, surface: impl Into<String>) -> Self {
141 self.inner.surface = Some(surface.into());
142 self
143 }
144
145 pub fn available_if(mut self, condition: impl Into<String>) -> Self {
147 self.inner.available_if.push(condition.into());
148 self
149 }
150
151 pub fn var(mut self, is_var: bool) -> Self {
153 self.inner.var = is_var;
154 self
155 }
156
157 pub fn var_min(mut self, min: usize) -> Self {
159 self.inner.var_min = Some(min);
160 self
161 }
162
163 pub fn var_max(mut self, max: usize) -> Self {
165 self.inner.var_max = Some(max);
166 self
167 }
168
169 pub fn required(mut self, is_required: bool) -> Self {
171 self.inner.required = is_required;
172 self
173 }
174
175 pub fn required_if(mut self, flag: impl Into<String>) -> Self {
177 self.inner.required_if.push(flag.into());
178 self
179 }
180
181 pub fn required_if_any<I, S>(mut self, flags: I) -> Self
183 where
184 I: IntoIterator<Item = S>,
185 S: Into<String>,
186 {
187 self.inner
188 .required_if
189 .extend(flags.into_iter().map(Into::into));
190 self
191 }
192
193 pub fn required_if_eq(mut self, selector: impl Into<String>, value: impl Into<String>) -> Self {
195 self.inner.required_if_eq.push(SpecRequiredIfEq {
196 selector: selector.into(),
197 value: value.into(),
198 });
199 self
200 }
201
202 pub fn required_if_eq_all<I, S, V>(mut self, conditions: I) -> Self
204 where
205 I: IntoIterator<Item = (S, V)>,
206 S: Into<String>,
207 V: Into<String>,
208 {
209 self.inner
210 .required_if_eq_all
211 .extend(
212 conditions
213 .into_iter()
214 .map(|(selector, value)| SpecRequiredIfEq {
215 selector: selector.into(),
216 value: value.into(),
217 }),
218 );
219 self
220 }
221
222 pub fn required_unless(mut self, flag: impl Into<String>) -> Self {
224 self.inner.required_unless.push(flag.into());
225 self
226 }
227
228 pub fn required_unless_any<I, S>(mut self, flags: I) -> Self
230 where
231 I: IntoIterator<Item = S>,
232 S: Into<String>,
233 {
234 self.inner
235 .required_unless
236 .extend(flags.into_iter().map(Into::into));
237 self
238 }
239
240 pub fn required_unless_all<I, S>(mut self, flags: I) -> Self
242 where
243 I: IntoIterator<Item = S>,
244 S: Into<String>,
245 {
246 self.inner
247 .required_unless_all
248 .extend(flags.into_iter().map(Into::into));
249 self
250 }
251
252 pub fn global(mut self, is_global: bool) -> Self {
254 self.inner.global = is_global;
255 self
256 }
257
258 pub fn hide(mut self, is_hidden: bool) -> Self {
260 self.inner.hide = is_hidden;
261 self
262 }
263
264 pub fn count(mut self, is_count: bool) -> Self {
266 self.inner.count = is_count;
267 self
268 }
269
270 pub fn allow_hyphen_values(mut self, allow: bool) -> Self {
272 self.allow_hyphen_values = allow;
273 if let Some(arg) = &mut self.inner.arg {
274 arg.double_dash = if allow {
275 crate::spec::arg::SpecDoubleDashChoices::Automatic
276 } else {
277 crate::spec::arg::SpecDoubleDashChoices::Optional
278 };
279 }
280 self
281 }
282
283 pub fn require_equals(mut self, require: bool) -> Self {
285 self.inner.require_equals = require;
286 self
287 }
288
289 pub fn value_optional(mut self, optional: bool) -> Self {
291 self.inner.value_optional = optional;
292 self
293 }
294
295 pub fn bool_value(mut self, enabled: bool) -> Self {
297 self.inner.bool_value = enabled;
298 self
299 }
300
301 pub fn default_missing(mut self, value: impl Into<String>) -> Self {
303 self.inner.default_missing = Some(value.into());
304 self
305 }
306
307 pub fn arg(mut self, arg: SpecArg) -> Self {
309 self.inner.arg = Some(arg);
310 if self.allow_hyphen_values {
311 if let Some(arg) = &mut self.inner.arg {
312 arg.double_dash = crate::spec::arg::SpecDoubleDashChoices::Automatic;
313 }
314 }
315 self
316 }
317
318 pub fn negate(mut self, negate: impl Into<String>) -> Self {
320 self.inner.negate = Some(negate.into());
321 self
322 }
323
324 pub fn override_with(mut self, flag: impl Into<String>) -> Self {
326 self.inner.overrides.push(flag.into());
327 self
328 }
329
330 pub fn overrides_with<I, S>(mut self, flags: I) -> Self
332 where
333 I: IntoIterator<Item = S>,
334 S: Into<String>,
335 {
336 self.inner
337 .overrides
338 .extend(flags.into_iter().map(Into::into));
339 self
340 }
341
342 pub fn require(mut self, flag: impl Into<String>) -> Self {
344 self.inner.requires.push(flag.into());
345 self
346 }
347
348 pub fn requires<I, S>(mut self, flags: I) -> Self
350 where
351 I: IntoIterator<Item = S>,
352 S: Into<String>,
353 {
354 self.inner
355 .requires
356 .extend(flags.into_iter().map(Into::into));
357 self
358 }
359
360 pub fn requires_if(mut self, value: impl Into<String>, flag: impl Into<String>) -> Self {
362 self.inner.requires_if.push(SpecRequiresIf {
363 value: value.into(),
364 requires: flag.into(),
365 });
366 self
367 }
368
369 pub fn requires_ifs<I, V, S>(mut self, requirements: I) -> Self
371 where
372 I: IntoIterator<Item = (V, S)>,
373 V: Into<String>,
374 S: Into<String>,
375 {
376 self.inner
377 .requires_if
378 .extend(
379 requirements
380 .into_iter()
381 .map(|(value, requires)| SpecRequiresIf {
382 value: value.into(),
383 requires: requires.into(),
384 }),
385 );
386 self
387 }
388
389 pub fn default_if(mut self, selector: impl Into<String>, value: impl Into<String>) -> Self {
393 self.inner.default_if.push(SpecDefaultIf {
394 selector: selector.into(),
395 when: None,
396 value: value.into(),
397 });
398 self
399 }
400
401 pub fn default_if_eq(
405 mut self,
406 selector: impl Into<String>,
407 when: impl Into<String>,
408 value: impl Into<String>,
409 ) -> Self {
410 self.inner.default_if.push(SpecDefaultIf {
411 selector: selector.into(),
412 when: Some(when.into()),
413 value: value.into(),
414 });
415 self
416 }
417
418 pub fn default_ifs<I>(mut self, conditions: I) -> Self
420 where
421 I: IntoIterator<Item = SpecDefaultIf>,
422 {
423 self.inner.default_if.extend(conditions);
424 self
425 }
426
427 pub fn help_heading(mut self, help_heading: impl Into<String>) -> Self {
429 self.inner.help_heading = Some(help_heading.into());
430 self
431 }
432
433 pub fn action(mut self, action: crate::SpecFlagAction) -> Self {
435 self.inner.action = action;
436 self
437 }
438
439 pub fn env(mut self, env: impl Into<String>) -> Self {
440 self.inner.env = Some(env.into());
441 self
442 }
443
444 pub fn env_fallback(mut self, env: impl Into<String>) -> Self {
446 self.inner.env_fallback.push(env.into());
447 self
448 }
449
450 pub fn deprecated_env(mut self, env: impl Into<String>) -> Self {
452 self.inner.deprecated_env.push(env.into());
453 self
454 }
455
456 pub fn deprecated(mut self, msg: impl Into<String>) -> Self {
458 self.inner.deprecated = Some(msg.into());
459 self
460 }
461
462 pub fn deprecated_warn_at(mut self, version: impl Into<String>) -> Self {
463 self.inner.deprecated_warn_at = Some(version.into());
464 self
465 }
466
467 pub fn deprecated_remove_at(mut self, version: impl Into<String>) -> Self {
468 self.inner.deprecated_remove_at = Some(version.into());
469 self
470 }
471
472 pub fn usage(mut self, usage: impl Into<String>) -> Self {
474 self.inner.usage = usage.into();
475 self
476 }
477
478 pub fn help_first_line(mut self, text: impl Into<String>) -> Self {
480 self.inner.help_first_line = Some(text.into());
481 self
482 }
483
484 pub fn effect(mut self, effect: SpecCommandEffect) -> Self {
486 self.inner.effect = Some(effect);
487 self
488 }
489
490 #[must_use]
492 pub fn build(mut self) -> SpecFlag {
493 if self.allow_hyphen_values {
494 if let Some(arg) = &mut self.inner.arg {
495 arg.double_dash = crate::spec::arg::SpecDoubleDashChoices::Automatic;
496 }
497 }
498 if self.inner.default_missing.is_some() {
499 if let Some(arg) = &mut self.inner.arg {
500 arg.required = false;
501 }
502 }
503 self.inner.usage = self.inner.usage();
504 if self.inner.name.is_empty() {
505 if let Some(long) = self.inner.long.first() {
507 self.inner.name = long.clone();
508 } else if let Some(short) = self.inner.short.first() {
509 self.inner.name = short.to_string();
510 }
511 }
512 self.inner
513 }
514}
515
516#[derive(Debug, Default, Clone)]
518pub struct SpecArgBuilder {
519 inner: SpecArg,
520}
521
522impl SpecArgBuilder {
523 pub fn new() -> Self {
525 Self::default()
526 }
527
528 pub fn surface(mut self, surface: impl Into<String>) -> Self {
530 self.inner.surface = Some(surface.into());
531 self
532 }
533
534 pub fn available_if(mut self, condition: impl Into<String>) -> Self {
536 self.inner.available_if.push(condition.into());
537 self
538 }
539
540 pub fn name(mut self, name: impl Into<String>) -> Self {
542 self.inner.name = name.into();
543 self
544 }
545
546 pub fn sigil(mut self, sigil: impl Into<String>) -> Self {
548 self.inner.sigil = Some(sigil.into());
549 self
550 }
551
552 pub fn value_names<I, S>(mut self, names: I) -> Self
554 where
555 I: IntoIterator<Item = S>,
556 S: Into<String>,
557 {
558 self.inner.value_names = names.into_iter().map(Into::into).collect();
559 if let Some(first) = self.inner.value_names.first() {
560 self.inner.name.clone_from(first);
561 }
562 if self.inner.value_names.len() > 1 {
563 let arity = self.inner.value_names.len();
564 self.inner.var = true;
565 self.inner.var_min = Some(arity);
566 self.inner.var_max = Some(arity);
567 }
568 self
569 }
570
571 pub fn default_value(mut self, value: impl Into<String>) -> Self {
573 self.inner.default.push(value.into());
574 self.inner.required = false;
575 self
576 }
577
578 pub fn default_values<I, S>(mut self, values: I) -> Self
580 where
581 I: IntoIterator<Item = S>,
582 S: Into<String>,
583 {
584 self.inner
585 .default
586 .extend(values.into_iter().map(Into::into));
587 if !self.inner.default.is_empty() {
588 self.inner.required = false;
589 }
590 self
591 }
592
593 pub fn help(mut self, text: impl Into<String>) -> Self {
595 self.inner.help = Some(text.into());
596 self
597 }
598
599 pub fn help_long(mut self, text: impl Into<String>) -> Self {
601 self.inner.help_long = Some(text.into());
602 self
603 }
604
605 pub fn help_md(mut self, text: impl Into<String>) -> Self {
607 self.inner.help_md = Some(text.into());
608 self
609 }
610
611 pub fn note(mut self, text: impl Into<String>) -> Self {
613 self.inner.admonitions.push(SpecAdmonition::note(text));
614 self
615 }
616
617 pub fn warning(mut self, text: impl Into<String>) -> Self {
619 self.inner.admonitions.push(SpecAdmonition::warning(text));
620 self
621 }
622
623 pub fn var(mut self, is_var: bool) -> Self {
625 self.inner.var = is_var;
626 self
627 }
628
629 pub fn var_min(mut self, min: usize) -> Self {
631 self.inner.var_min = Some(min);
632 self
633 }
634
635 pub fn var_max(mut self, max: usize) -> Self {
637 self.inner.var_max = Some(max);
638 self
639 }
640
641 pub fn required(mut self, is_required: bool) -> Self {
643 self.inner.required = is_required;
644 self
645 }
646
647 pub fn requires<I, S>(mut self, selectors: I) -> Self
649 where
650 I: IntoIterator<Item = S>,
651 S: Into<String>,
652 {
653 self.inner
654 .requires
655 .extend(selectors.into_iter().map(Into::into));
656 self
657 }
658
659 pub fn required_if_any<I, S>(mut self, selectors: I) -> Self
661 where
662 I: IntoIterator<Item = S>,
663 S: Into<String>,
664 {
665 self.inner
666 .required_if
667 .extend(selectors.into_iter().map(Into::into));
668 self
669 }
670
671 pub fn required_if_eq(mut self, selector: impl Into<String>, value: impl Into<String>) -> Self {
673 self.inner.required_if_eq.push(SpecRequiredIfEq {
674 selector: selector.into(),
675 value: value.into(),
676 });
677 self
678 }
679
680 pub fn required_if_eq_all<I, S, V>(mut self, conditions: I) -> Self
682 where
683 I: IntoIterator<Item = (S, V)>,
684 S: Into<String>,
685 V: Into<String>,
686 {
687 self.inner
688 .required_if_eq_all
689 .extend(
690 conditions
691 .into_iter()
692 .map(|(selector, value)| SpecRequiredIfEq {
693 selector: selector.into(),
694 value: value.into(),
695 }),
696 );
697 self
698 }
699
700 pub fn required_unless_any<I, S>(mut self, selectors: I) -> Self
702 where
703 I: IntoIterator<Item = S>,
704 S: Into<String>,
705 {
706 self.inner
707 .required_unless
708 .extend(selectors.into_iter().map(Into::into));
709 self
710 }
711
712 pub fn required_unless_all<I, S>(mut self, selectors: I) -> Self
714 where
715 I: IntoIterator<Item = S>,
716 S: Into<String>,
717 {
718 self.inner
719 .required_unless_all
720 .extend(selectors.into_iter().map(Into::into));
721 self
722 }
723
724 pub fn hide(mut self, is_hidden: bool) -> Self {
726 self.inner.hide = is_hidden;
727 self
728 }
729
730 pub fn help_heading(mut self, help_heading: impl Into<String>) -> Self {
733 self.inner.help_heading = Some(help_heading.into());
734 self
735 }
736
737 pub fn env(mut self, env: impl Into<String>) -> Self {
738 self.inner.env = Some(env.into());
739 self
740 }
741
742 pub fn env_fallback(mut self, env: impl Into<String>) -> Self {
744 self.inner.env_fallback.push(env.into());
745 self
746 }
747
748 pub fn deprecated_env(mut self, env: impl Into<String>) -> Self {
750 self.inner.deprecated_env.push(env.into());
751 self
752 }
753
754 pub fn double_dash(mut self, behavior: SpecDoubleDashChoices) -> Self {
756 self.inner.double_dash = behavior;
757 self
758 }
759
760 pub fn choices<I, S>(mut self, choices: I) -> Self
762 where
763 I: IntoIterator<Item = S>,
764 S: Into<String>,
765 {
766 let spec_choices = self.inner.choices.get_or_insert_with(SpecChoices::default);
767 #[cfg(feature = "unstable_choices_env")]
768 let env = spec_choices.env().map(ToString::to_string);
769 spec_choices.choices = choices.into_iter().map(Into::into).collect();
770 #[cfg(feature = "unstable_choices_env")]
771 spec_choices.set_env(env);
772 self
773 }
774
775 pub fn validate(mut self, expression: impl Into<String>) -> Self {
777 self.inner.validate = Some(expression.into());
778 self
779 }
780
781 pub fn validate_error(mut self, message: impl Into<String>) -> Self {
783 self.inner.validate_error = Some(message.into());
784 self
785 }
786
787 #[cfg(feature = "unstable_choices_env")]
789 pub fn choices_env(mut self, env: impl Into<String>) -> Self {
790 let choices = self.inner.choices.get_or_insert_with(SpecChoices::default);
791 choices.set_env(Some(env.into()));
792 self
793 }
794
795 pub fn usage(mut self, usage: impl Into<String>) -> Self {
797 self.inner.usage = usage.into();
798 self
799 }
800
801 pub fn help_first_line(mut self, text: impl Into<String>) -> Self {
803 self.inner.help_first_line = Some(text.into());
804 self
805 }
806
807 pub fn effect(mut self, effect: SpecCommandEffect) -> Self {
809 self.inner.effect = Some(effect);
810 self
811 }
812
813 #[must_use]
815 pub fn build(mut self) -> SpecArg {
816 if self.inner.validate.is_none() {
817 self.inner.validate_error = None;
818 }
819 if self.inner.value_names.len() > 1 {
820 let arity = self.inner.value_names.len();
821 self.inner.var = true;
822 self.inner.var_min = Some(arity);
823 self.inner.var_max = Some(arity);
824 }
825 self.inner.usage = self.inner.usage();
826 self.inner
827 }
828}
829
830#[derive(Debug, Default, Clone)]
832pub struct SpecCommandBuilder {
833 inner: SpecCommand,
834}
835
836impl SpecCommandBuilder {
837 pub fn surface(mut self, surface: impl Into<String>) -> Self {
839 self.inner.surface = Some(surface.into());
840 self
841 }
842
843 pub fn available_if(mut self, condition: impl Into<String>) -> Self {
845 self.inner.available_if.push(condition.into());
846 self
847 }
848 pub fn new() -> Self {
850 Self::default()
851 }
852
853 pub fn name(mut self, name: impl Into<String>) -> Self {
855 self.inner.name = name.into();
856 self
857 }
858
859 pub fn alias(mut self, alias: impl Into<String>) -> Self {
861 self.inner.aliases.push(alias.into());
862 self
863 }
864
865 pub fn aliases<I, S>(mut self, aliases: I) -> Self
867 where
868 I: IntoIterator<Item = S>,
869 S: Into<String>,
870 {
871 self.inner
872 .aliases
873 .extend(aliases.into_iter().map(Into::into));
874 self
875 }
876
877 pub fn hidden_alias(mut self, alias: impl Into<String>) -> Self {
879 self.inner.hidden_aliases.push(alias.into());
880 self
881 }
882
883 pub fn hidden_aliases<I, S>(mut self, aliases: I) -> Self
885 where
886 I: IntoIterator<Item = S>,
887 S: Into<String>,
888 {
889 self.inner
890 .hidden_aliases
891 .extend(aliases.into_iter().map(Into::into));
892 self
893 }
894
895 pub fn flag(mut self, flag: SpecFlag) -> Self {
897 self.inner.flags.push(flag);
898 self
899 }
900
901 pub fn flags(mut self, flags: impl IntoIterator<Item = SpecFlag>) -> Self {
903 self.inner.flags.extend(flags);
904 self
905 }
906
907 pub fn arg(mut self, arg: SpecArg) -> Self {
909 self.inner.args.push(arg);
910 self
911 }
912
913 pub fn args(mut self, args: impl IntoIterator<Item = SpecArg>) -> Self {
915 self.inner.args.extend(args);
916 self
917 }
918
919 pub fn help(mut self, text: impl Into<String>) -> Self {
921 self.inner.help = Some(text.into());
922 self
923 }
924
925 pub fn help_long(mut self, text: impl Into<String>) -> Self {
927 self.inner.help_long = Some(text.into());
928 self
929 }
930
931 pub fn help_md(mut self, text: impl Into<String>) -> Self {
933 self.inner.help_md = Some(text.into());
934 self
935 }
936
937 pub fn hide(mut self, is_hidden: bool) -> Self {
939 self.inner.hide = is_hidden;
940 self
941 }
942
943 pub fn subcommand_required(mut self, required: bool) -> Self {
945 self.inner.subcommand_required = required;
946 self
947 }
948
949 pub fn subcommand_help_heading(mut self, heading: impl Into<String>) -> Self {
951 self.inner.subcommand_help_heading = Some(heading.into());
952 self
953 }
954
955 pub fn subcommand_value_name(mut self, name: impl Into<String>) -> Self {
957 self.inner.subcommand_value_name = Some(name.into());
958 self
959 }
960
961 pub fn term_width(mut self, width: usize) -> Self {
963 self.inner.term_width = Some(width);
964 self
965 }
966
967 pub fn max_term_width(mut self, width: usize) -> Self {
969 self.inner.max_term_width = Some(width);
970 self
971 }
972
973 pub fn external_subcommand(mut self, enabled: bool) -> Self {
975 self.inner.external_subcommand = enabled;
976 self
977 }
978
979 pub fn disable_help_flag(mut self, disabled: bool) -> Self {
981 self.inner.disable_help_flag = disabled;
982 self
983 }
984
985 pub fn disable_help_subcommand(mut self, disabled: bool) -> Self {
987 self.inner.disable_help_subcommand = disabled;
988 self
989 }
990
991 pub fn disable_version_flag(mut self, disabled: bool) -> Self {
993 self.inner.disable_version_flag = disabled;
994 self
995 }
996
997 pub fn args_override_self(mut self, enabled: bool) -> Self {
1001 self.inner.args_override_self = enabled;
1002 self
1003 }
1004
1005 pub fn subcommand_negates_reqs(mut self, enabled: bool) -> Self {
1007 self.inner.subcommand_negates_reqs = enabled;
1008 self
1009 }
1010
1011 pub fn args_conflicts_with_subcommands(mut self, enabled: bool) -> Self {
1013 self.inner.args_conflicts_with_subcommands = enabled;
1014 self
1015 }
1016
1017 pub fn subcommand_precedence_over_arg(mut self, enabled: bool) -> Self {
1018 self.inner.subcommand_precedence_over_arg = enabled;
1019 self
1020 }
1021
1022 pub fn allow_missing_positional(mut self, enabled: bool) -> Self {
1023 self.inner.allow_missing_positional = enabled;
1024 self
1025 }
1026
1027 pub fn effect(mut self, effect: SpecCommandEffect) -> Self {
1029 self.inner.effect = Some(effect);
1030 self
1031 }
1032
1033 pub fn deprecated(mut self, msg: impl Into<String>) -> Self {
1035 self.inner.deprecated = Some(msg.into());
1036 self
1037 }
1038
1039 pub fn deprecated_warn_at(mut self, version: impl Into<String>) -> Self {
1040 self.inner.deprecated_warn_at = Some(version.into());
1041 self
1042 }
1043
1044 pub fn deprecated_remove_at(mut self, version: impl Into<String>) -> Self {
1045 self.inner.deprecated_remove_at = Some(version.into());
1046 self
1047 }
1048
1049 pub fn restart_token(mut self, token: impl Into<String>) -> Self {
1052 self.inner.restart_token = Some(token.into());
1053 self
1054 }
1055
1056 pub fn subcommand(mut self, cmd: SpecCommand) -> Self {
1058 self.inner.subcommands.insert(cmd.name.clone(), cmd);
1059 self
1060 }
1061
1062 pub fn subcommands(mut self, cmds: impl IntoIterator<Item = SpecCommand>) -> Self {
1064 for cmd in cmds {
1065 self.inner.subcommands.insert(cmd.name.clone(), cmd);
1066 }
1067 self
1068 }
1069
1070 pub fn before_help(mut self, text: impl Into<String>) -> Self {
1072 self.inner.before_help = Some(text.into());
1073 self
1074 }
1075
1076 pub fn before_help_long(mut self, text: impl Into<String>) -> Self {
1078 self.inner.before_help_long = Some(text.into());
1079 self
1080 }
1081
1082 pub fn before_help_md(mut self, text: impl Into<String>) -> Self {
1084 self.inner.before_help_md = Some(text.into());
1085 self
1086 }
1087
1088 pub fn after_help(mut self, text: impl Into<String>) -> Self {
1090 self.inner.after_help = Some(text.into());
1091 self
1092 }
1093
1094 pub fn after_help_long(mut self, text: impl Into<String>) -> Self {
1096 self.inner.after_help_long = Some(text.into());
1097 self
1098 }
1099
1100 pub fn after_help_md(mut self, text: impl Into<String>) -> Self {
1102 self.inner.after_help_md = Some(text.into());
1103 self
1104 }
1105
1106 pub fn example(mut self, code: impl Into<String>) -> Self {
1108 self.inner.examples.push(SpecExample::new(code.into()));
1109 self
1110 }
1111
1112 pub fn example_with_help(
1114 mut self,
1115 code: impl Into<String>,
1116 header: impl Into<String>,
1117 help: impl Into<String>,
1118 ) -> Self {
1119 let mut example = SpecExample::new(code.into());
1120 example.header = Some(header.into());
1121 example.help = Some(help.into());
1122 self.inner.examples.push(example);
1123 self
1124 }
1125
1126 #[must_use]
1128 pub fn build(mut self) -> SpecCommand {
1129 self.inner.usage = self.inner.usage();
1130 self.inner
1131 }
1132}
1133
1134#[cfg(test)]
1135mod tests {
1136 use super::*;
1137
1138 #[test]
1139 fn test_flag_builder_basic() {
1140 let flag = SpecFlagBuilder::new()
1141 .name("verbose")
1142 .short('v')
1143 .long("verbose")
1144 .help("Enable verbose output")
1145 .build();
1146
1147 assert_eq!(flag.name, "verbose");
1148 assert_eq!(flag.short, vec!['v']);
1149 assert_eq!(flag.long, vec!["verbose".to_string()]);
1150 assert_eq!(flag.help, Some("Enable verbose output".to_string()));
1151 }
1152
1153 #[test]
1154 fn test_flag_builder_multiple_values() {
1155 let flag = SpecFlagBuilder::new()
1156 .shorts(['v', 'V'])
1157 .longs(["verbose", "loud"])
1158 .default_values(["info", "warn"])
1159 .build();
1160
1161 assert_eq!(flag.short, vec!['v', 'V']);
1162 assert_eq!(flag.long, vec!["verbose".to_string(), "loud".to_string()]);
1163 assert_eq!(flag.default, vec!["info".to_string(), "warn".to_string()]);
1164 assert!(!flag.required); }
1166
1167 #[test]
1168 fn test_flag_builder_variadic() {
1169 let flag = SpecFlagBuilder::new()
1170 .long("file")
1171 .var(true)
1172 .var_min(1)
1173 .var_max(10)
1174 .build();
1175
1176 assert!(flag.var);
1177 assert_eq!(flag.var_min, Some(1));
1178 assert_eq!(flag.var_max, Some(10));
1179 }
1180
1181 #[test]
1182 fn test_flag_builder_conditional_requirements() {
1183 let flag = SpecFlagBuilder::new()
1184 .long("config")
1185 .requires_if("special.toml", "--key")
1186 .requires_ifs([("remote.toml", "--token"), ("signed.toml", "--identity")])
1187 .build();
1188
1189 assert_eq!(
1190 flag.requires_if,
1191 [
1192 SpecRequiresIf {
1193 value: "special.toml".into(),
1194 requires: "--key".into(),
1195 },
1196 SpecRequiresIf {
1197 value: "remote.toml".into(),
1198 requires: "--token".into(),
1199 },
1200 SpecRequiresIf {
1201 value: "signed.toml".into(),
1202 requires: "--identity".into(),
1203 },
1204 ]
1205 );
1206 }
1207
1208 #[test]
1209 fn test_flag_builder_conditional_defaults() {
1210 let flag = SpecFlagBuilder::new()
1211 .long("bin-names")
1212 .default_if("--json", "true")
1213 .default_if_eq("--output", "json", "pretty")
1214 .build();
1215
1216 assert_eq!(
1217 flag.default_if,
1218 [
1219 SpecDefaultIf {
1220 selector: "--json".into(),
1221 when: None,
1222 value: "true".into(),
1223 },
1224 SpecDefaultIf {
1225 selector: "--output".into(),
1226 when: Some("json".into()),
1227 value: "pretty".into(),
1228 },
1229 ]
1230 );
1231 }
1232
1233 #[test]
1234 fn test_flag_builder_name_derivation() {
1235 let flag = SpecFlagBuilder::new().short('v').long("verbose").build();
1236
1237 assert_eq!(flag.name, "verbose");
1239
1240 let flag2 = SpecFlagBuilder::new().short('v').build();
1241
1242 assert_eq!(flag2.name, "v");
1244 }
1245
1246 #[test]
1247 fn test_arg_builder_basic() {
1248 let arg = SpecArgBuilder::new()
1249 .name("file")
1250 .help("Input file")
1251 .required(true)
1252 .build();
1253
1254 assert_eq!(arg.name, "file");
1255 assert_eq!(arg.help, Some("Input file".to_string()));
1256 assert!(arg.required);
1257 }
1258
1259 #[test]
1260 fn test_arg_builder_variadic() {
1261 let arg = SpecArgBuilder::new()
1262 .name("files")
1263 .var(true)
1264 .var_min(1)
1265 .var_max(10)
1266 .help("Input files")
1267 .build();
1268
1269 assert_eq!(arg.name, "files");
1270 assert!(arg.var);
1271 assert_eq!(arg.var_min, Some(1));
1272 assert_eq!(arg.var_max, Some(10));
1273 }
1274
1275 #[test]
1276 fn test_arg_builder_defaults() {
1277 let arg = SpecArgBuilder::new()
1278 .name("file")
1279 .default_values(["a.txt", "b.txt"])
1280 .build();
1281
1282 assert_eq!(arg.default, vec!["a.txt".to_string(), "b.txt".to_string()]);
1283 assert!(!arg.required);
1284 }
1285
1286 #[test]
1287 fn test_arg_builder_drops_validation_error_without_expression() {
1288 let arg = SpecArgBuilder::new()
1289 .name("port")
1290 .validate_error("must be a valid port")
1291 .build();
1292
1293 assert!(arg.validate_error.is_none());
1294 }
1295
1296 #[test]
1297 fn test_command_builder_basic() {
1298 let cmd = SpecCommandBuilder::new()
1299 .name("install")
1300 .help("Install packages")
1301 .build();
1302
1303 assert_eq!(cmd.name, "install");
1304 assert_eq!(cmd.help, Some("Install packages".to_string()));
1305 }
1306
1307 #[test]
1308 fn test_command_builder_aliases() {
1309 let cmd = SpecCommandBuilder::new()
1310 .name("install")
1311 .alias("i")
1312 .aliases(["add", "get"])
1313 .hidden_aliases(["inst"])
1314 .build();
1315
1316 assert_eq!(
1317 cmd.aliases,
1318 vec!["i".to_string(), "add".to_string(), "get".to_string()]
1319 );
1320 assert_eq!(cmd.hidden_aliases, vec!["inst".to_string()]);
1321 }
1322
1323 #[test]
1324 fn test_command_builder_with_flags_and_args() {
1325 let flag = SpecFlagBuilder::new().short('f').long("force").build();
1326
1327 let arg = SpecArgBuilder::new().name("package").required(true).build();
1328
1329 let cmd = SpecCommandBuilder::new()
1330 .name("install")
1331 .flag(flag)
1332 .arg(arg)
1333 .build();
1334
1335 assert_eq!(cmd.flags.len(), 1);
1336 assert_eq!(cmd.flags[0].name, "force");
1337 assert_eq!(cmd.args.len(), 1);
1338 assert_eq!(cmd.args[0].name, "package");
1339 }
1340
1341 #[test]
1342 fn test_arg_builder_choices() {
1343 let arg = SpecArgBuilder::new()
1344 .name("format")
1345 .choices(["json", "yaml", "toml"])
1346 .build();
1347
1348 assert!(arg.choices.is_some());
1349 let choices = arg.choices.unwrap();
1350 assert_eq!(
1351 choices.choices,
1352 vec!["json".to_string(), "yaml".to_string(), "toml".to_string()]
1353 );
1354 assert_eq!(choices.env(), None);
1355 }
1356
1357 #[cfg(feature = "unstable_choices_env")]
1358 #[test]
1359 fn test_arg_builder_choices_env() {
1360 let arg = SpecArgBuilder::new()
1361 .name("env")
1362 .choices(["local"])
1363 .choices_env("DEPLOY_ENVS")
1364 .build();
1365
1366 let choices = arg.choices.unwrap();
1367 assert_eq!(choices.choices, vec!["local".to_string()]);
1368 assert_eq!(choices.env(), Some("DEPLOY_ENVS"));
1369 }
1370
1371 #[cfg(feature = "unstable_choices_env")]
1372 #[test]
1373 fn test_arg_builder_choices_preserves_choices_env() {
1374 let arg = SpecArgBuilder::new()
1375 .name("env")
1376 .choices_env("DEPLOY_ENVS")
1377 .choices(["local"])
1378 .build();
1379
1380 let choices = arg.choices.unwrap();
1381 assert_eq!(choices.choices, vec!["local".to_string()]);
1382 assert_eq!(choices.env(), Some("DEPLOY_ENVS"));
1383 }
1384
1385 #[test]
1386 fn test_command_builder_subcommands() {
1387 let sub1 = SpecCommandBuilder::new().name("sub1").build();
1388 let sub2 = SpecCommandBuilder::new().name("sub2").build();
1389
1390 let cmd = SpecCommandBuilder::new()
1391 .name("main")
1392 .subcommand(sub1)
1393 .subcommand(sub2)
1394 .build();
1395
1396 assert_eq!(cmd.subcommands.len(), 2);
1397 assert!(cmd.subcommands.contains_key("sub1"));
1398 assert!(cmd.subcommands.contains_key("sub2"));
1399 }
1400
1401 #[test]
1402 fn test_command_builder_before_after_help() {
1403 let cmd = SpecCommandBuilder::new()
1404 .name("test")
1405 .before_help("Before help text")
1406 .before_help_long("Before help long text")
1407 .after_help("After help text")
1408 .after_help_long("After help long text")
1409 .build();
1410
1411 assert_eq!(cmd.before_help, Some("Before help text".to_string()));
1412 assert_eq!(
1413 cmd.before_help_long,
1414 Some("Before help long text".to_string())
1415 );
1416 assert_eq!(cmd.after_help, Some("After help text".to_string()));
1417 assert_eq!(
1418 cmd.after_help_long,
1419 Some("After help long text".to_string())
1420 );
1421 }
1422
1423 #[test]
1424 fn test_command_builder_examples() {
1425 let cmd = SpecCommandBuilder::new()
1426 .name("test")
1427 .example("mycli run")
1428 .example_with_help("mycli build", "Build example", "Build the project")
1429 .build();
1430
1431 assert_eq!(cmd.examples.len(), 2);
1432 assert_eq!(cmd.examples[0].code, "mycli run");
1433 assert_eq!(cmd.examples[1].code, "mycli build");
1434 assert_eq!(cmd.examples[1].header, Some("Build example".to_string()));
1435 assert_eq!(cmd.examples[1].help, Some("Build the project".to_string()));
1436 }
1437}