1use proc_macro::TokenStream;
19use proc_macro2::{Span, TokenStream as TokenStream2};
20use quote::{format_ident, quote};
21use syn::{
22 Data, DeriveInput, Fields, GenericArgument, Ident, LitByteStr, LitChar, LitStr, PathArguments,
23 Type, parse_macro_input, spanned::Spanned as _,
24};
25
26#[proc_macro_derive(Args, attributes(arg, winnow_args))]
179pub fn derive_args(input: TokenStream) -> TokenStream {
180 let input = parse_macro_input!(input as DeriveInput);
181 expand(&input)
182 .unwrap_or_else(syn::Error::into_compile_error)
183 .into()
184}
185
186#[proc_macro_derive(Subcommand, attributes(arg, winnow_args))]
238pub fn derive_subcommand(input: TokenStream) -> TokenStream {
239 let input = parse_macro_input!(input as DeriveInput);
240 expand_subcommand(&input)
241 .unwrap_or_else(syn::Error::into_compile_error)
242 .into()
243}
244
245fn expand_subcommand(input: &DeriveInput) -> syn::Result<TokenStream2> {
247 let Data::Enum(data) = &input.data else {
248 return Err(syn::Error::new(
249 input.span(),
250 "`Subcommand` can only be derived for enums",
251 ));
252 };
253 let case = rename_all(input)?;
254 let mut names: Vec<(String, &Ident)> = Vec::new();
255 let mut arms = Vec::new();
256 let mut patterns = Vec::new();
257 let mut subs = Vec::new();
258 for variant in &data.variants {
259 let ident = &variant.ident;
260 let info = variant_names(variant, &mut names, case)?;
261 let pattern = byte_patterns(&info.names);
262 let primary = LitStr::new(&info.names[0], Span::call_site());
263 let inner = match &variant.fields {
264 Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
265 let ty = &fields.unnamed[0].ty;
266 Some(boxed(ty).unwrap_or(ty).clone())
267 }
268 _ => None,
269 };
270 let command = match &inner {
271 Some(ty) => quote!(<#ty as ::winnow_args::Args>::HELP),
272 None => {
273 let (doc, long_doc) = docs(&variant.attrs);
275 let long_about = text(if doc == info.about {
276 &long_doc
277 } else {
278 &info.about
279 });
280 let about = text(&info.about);
281 quote!(&::winnow_args::help::Command {
282 name: "",
283 about: #about,
284 long_about: #long_about,
285 after_help: "",
286 after_long_help: "",
287 items: &[],
288 subcommands: &[],
289 subcommand_required: false,
290 help_flag: true,
291 help_short: true,
292 version: ::core::option::Option::None,
293 })
294 }
295 };
296 let about = match (&inner, info.about.is_empty()) {
297 (Some(ty), true) => quote!(<#ty as ::winnow_args::Args>::HELP.about),
298 _ => text(&info.about),
299 };
300 let shown = &info.names[1..=info.shown];
301 let all = &info.names;
302 let hide = info.hide;
303 subs.push(quote! {
304 ::winnow_args::help::Sub {
305 name: #primary,
306 aliases: &[#(#shown),*],
307 names: &[#(#all),*],
308 command: #command,
309 about: #about,
310 hide: #hide,
311 }
312 });
313 let read_payload = quote! {
315 |__item| match __item {
316 Self::#ident(__read) => {
317 let _ = __read;
318 }
319 #[allow(unreachable_patterns)]
320 _ => {}
321 }
322 };
323 let parse = match &variant.fields {
324 Fields::Unit => {
325 let index = subs.len() - 1;
326 quote! {
327 __wa::finish_with(
328 __input,
329 __globals,
330 <Self as ::winnow_args::Args>::HELP.subcommands[#index].command,
331 )
332 .map(|()| Self::#ident)
333 }
334 }
335 Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
336 let ty = &fields.unnamed[0].ty;
337 match boxed(ty) {
338 Some(inner) => quote! {
339 <#inner as ::winnow_args::Args>::parse_argv_with(__input, __globals)
340 .map(|v| Self::#ident(::std::boxed::Box::new(v)))
341 .inspect(#read_payload)
342 },
343 None => quote! {
344 <#ty as ::winnow_args::Args>::parse_argv_with(__input, __globals)
345 .map(Self::#ident)
346 .inspect(#read_payload)
347 },
348 }
349 }
350 _ => {
351 return Err(syn::Error::new(
352 variant.span(),
353 "a subcommand variant is a unit or holds one `Args` type",
354 ));
355 }
356 };
357 arms.push(quote!(#pattern => (#parse).map_err(|e| e.within(#primary)),));
358 patterns.push(pattern);
359 }
360 let (about, long_about) = docs(&input.attrs);
361 let (about, long_about) = (text(&about), text(&long_about));
362
363 let name = &input.ident;
364 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
365 Ok(quote! {
366 impl #impl_generics ::winnow_args::Subcommand for #name #ty_generics #where_clause {
367 #[inline]
368 fn has(__name: &[u8]) -> bool {
369 matches!(__name, #(#patterns)|*)
370 }
371
372 fn parse_subcommand(
373 __name: &[u8],
374 __input: &mut ::winnow_args::Argv<'_>,
375 __globals: &mut dyn ::winnow_args::Globals,
376 ) -> ::core::result::Result<Self, ::winnow_args::Error> {
377 use ::winnow_args::__private as __wa;
378 match __name {
379 #(#arms)*
380 _ => ::core::result::Result::Err(__wa::Error::unexpected_arg(
381 __input.offset(),
382 ::std::string::String::from_utf8_lossy(__name),
383 )),
384 }
385 }
386 }
387
388 impl #impl_generics ::winnow_args::Args for #name #ty_generics #where_clause {
389 const HELP: &'static ::winnow_args::help::Command = &::winnow_args::help::Command {
390 name: "",
391 about: #about,
392 long_about: #long_about,
393 after_help: "",
394 after_long_help: "",
395 items: &[],
396 subcommands: &[#(#subs),*],
397 subcommand_required: true,
398 help_flag: true,
399 help_short: true,
400 version: ::core::option::Option::None,
401 };
402
403 fn parse_argv_with(
404 __input: &mut ::winnow_args::Argv<'_>,
405 __globals: &mut dyn ::winnow_args::Globals,
406 ) -> ::core::result::Result<Self, ::winnow_args::Error> {
407 use ::winnow_args::__private as __wa;
408 let __help = <Self as ::winnow_args::Args>::HELP;
409 loop {
411 if __input.is_empty() {
412 return ::core::result::Result::Err(
413 __wa::Error::missing_subcommand(__input.offset()),
414 );
415 }
416 let __arg = __wa::arg(__input)?;
417 match __arg {
418 __wa::Arg::Word(__word) => {
419 if <Self as __wa::Subcommand>::has(&**__word.value) {
420 return <Self as __wa::Subcommand>::parse_subcommand(
421 &**__word.value, __input, __globals,
422 );
423 }
424 if &**__word.value == b"help" {
425 return ::core::result::Result::Err(__wa::help_word(__help, __input));
426 }
427 }
428 __wa::Arg::Long(_) | __wa::Arg::Short(_)
429 if __globals.bind(&__arg, __input)? =>
430 {
431 continue;
432 }
433 __wa::Arg::Long(__flag) if __flag.name == b"help" => {
434 return ::core::result::Result::Err(__wa::Error::help(__help, true));
435 }
436 __wa::Arg::Short(__flag) if __flag.letter == 'h' => {
437 return ::core::result::Result::Err(__wa::Error::help(__help, false));
438 }
439 _ => {}
440 }
441 return ::core::result::Result::Err(__arg.unexpected());
442 }
443 }
444 }
445 })
446}
447
448#[proc_macro_derive(Occurrence, attributes(arg, winnow_args))]
514pub fn derive_occurrence(input: TokenStream) -> TokenStream {
515 let input = parse_macro_input!(input as DeriveInput);
516 expand_occurrence(&input)
517 .unwrap_or_else(syn::Error::into_compile_error)
518 .into()
519}
520
521#[proc_macro_derive(ValueEnum, attributes(arg, winnow_args))]
555pub fn derive_value_enum(input: TokenStream) -> TokenStream {
556 let input = parse_macro_input!(input as DeriveInput);
557 expand_value_enum(&input)
558 .unwrap_or_else(syn::Error::into_compile_error)
559 .into()
560}
561
562fn expand_occurrence(input: &DeriveInput) -> syn::Result<TokenStream2> {
565 let Data::Enum(data) = &input.data else {
566 return Err(syn::Error::new(
567 input.span(),
568 "`Occurrence` can only be derived for enums",
569 ));
570 };
571 let (mut long_arms, mut short_arms) = (Vec::new(), Vec::new());
572 let (mut single_dash, mut prefixes) = (Vec::new(), Vec::new());
573 let mut letters = Vec::new();
574 let (mut word, mut unknown, mut bundle) = (None, None, None);
575 let mut seen = std::collections::HashMap::new();
576 let mut help_items = Vec::new();
578 let (mut enum_hyphen_values, mut enum_keep_equals) = (false, false);
581 for attr in input.attrs.iter().filter(|a| is_ours(a)) {
582 attr.parse_nested_meta(|meta| {
583 if meta.path.is_ident("allow_hyphen_values") {
584 enum_hyphen_values = true;
585 Ok(())
586 } else if meta.path.is_ident("keep_equals") {
587 enum_keep_equals = true;
588 Ok(())
589 } else {
590 Err(meta.error("expected `allow_hyphen_values` or `keep_equals`"))
591 }
592 })?;
593 }
594 for variant in &data.variants {
595 let ident = &variant.ident;
596 let (mut short, mut longs, mut positional) = (None, Vec::new(), false);
597 let (mut two_dashes, mut prefix, mut value_name) = (false, false, None);
598 let (mut hyphen_values, mut negative_numbers) = (enum_hyphen_values, false);
599 let (mut require_equals, mut default_missing) = (false, None);
600 let mut keep_equals = enum_keep_equals;
601 let (mut skip, mut is_unknown, mut is_bundle) = (false, false, false);
602 for attr in variant.attrs.iter().filter(|a| is_ours(a)) {
603 attr.parse_nested_meta(|meta| {
604 if meta.path.is_ident("allow_hyphen_values") {
605 hyphen_values = true;
606 } else if meta.path.is_ident("allow_negative_numbers") {
607 negative_numbers = true;
608 } else if meta.path.is_ident("require_equals") {
609 require_equals = true;
610 } else if meta.path.is_ident("keep_equals") {
611 keep_equals = true;
612 } else if meta.path.is_ident("default_missing") {
613 default_missing = Some(meta.value()?.parse::<LitStr>()?.value());
614 } else if meta.path.is_ident("skip") {
615 skip = true;
616 } else if meta.path.is_ident("bundle") {
617 is_bundle = true;
618 } else if meta.path.is_ident("unknown") {
619 is_unknown = true;
620 } else if meta.path.is_ident("short") {
621 if short.is_some() {
622 return Err(meta.error("an `Occurrence` variant has one `short`"));
623 }
624 short = Some(meta.value()?.parse::<LitChar>()?.value());
625 } else if meta.path.is_ident("long") {
626 longs.push(if meta.input.peek(syn::Token![=]) {
627 meta.value()?.parse::<LitStr>()?.value()
628 } else {
629 kebab_case(&ident.to_string())
630 });
631 } else if meta.path.is_ident("alias") {
632 longs.extend(aliases(&meta)?);
633 } else if meta.path.is_ident("positional") {
634 positional = true;
635 } else if meta.path.is_ident("two_dashes") {
636 two_dashes = true;
637 } else if meta.path.is_ident("prefix") {
638 prefix = true;
639 } else if meta.path.is_ident("value_name") {
640 value_name = Some(meta.value()?.parse::<LitStr>()?.value());
641 } else {
642 return Err(meta.error(
643 "expected `short`, `long`, `alias`, `positional`, `two_dashes`, `prefix`, \
644 `value_name`, `allow_hyphen_values`, `allow_negative_numbers`, \
645 `require_equals`, `keep_equals`, `default_missing`, `skip`, `unknown` or `bundle`",
646 ));
647 }
648 Ok(())
649 })?;
650 }
651 let takes = match &variant.fields {
652 Fields::Unit => None,
653 Fields::Unnamed(f) if f.unnamed.len() == 1 => Some(&f.unnamed[0].ty),
654 _ => {
655 return Err(syn::Error::new(
656 variant.span(),
657 "an `Occurrence` variant is a unit (a switch) or holds one value",
658 ));
659 }
660 };
661 if skip {
662 continue;
664 }
665 let spanned = takes.and_then(|ty| {
667 last_segment(ty)
668 .filter(|s| s.ident == "Spanned")
669 .and_then(inner)
670 });
671 let wrap = |value: TokenStream2, offset: TokenStream2, attached: TokenStream2| {
672 if spanned.is_some() {
673 quote! {
674 ::winnow_args::value::Spanned {
675 value: #value,
676 offset: #offset,
677 attached: #attached,
678 }
679 }
680 } else {
681 value
682 }
683 };
684 if positional || is_unknown || is_bundle {
685 let Some(ty) = takes else {
686 return Err(syn::Error::new(
687 variant.span(),
688 "a positional, `unknown` or `bundle` variant holds its value",
689 ));
690 };
691 let ty = spanned.unwrap_or(ty);
692 let slot = if positional {
693 &mut word
694 } else if is_unknown {
695 &mut unknown
696 } else {
697 &mut bundle
698 };
699 if slot.is_some() {
700 return Err(syn::Error::new(
701 variant.span(),
702 "at most one positional, one `unknown` and one `bundle` variant",
703 ));
704 }
705 let name = value_name.unwrap_or_else(|| ident.to_string().to_uppercase());
706 if positional {
707 help_items.push(occurrence_item(
708 variant,
709 None,
710 &[],
711 Some(&name),
712 quote!(<#ty as ::winnow_args::FromArg>::CHOICES),
713 false,
714 false,
715 ));
716 }
717 let value = wrap(
718 quote!(__word.convert::<#ty>(#name)?),
719 quote!(__word.offset),
720 quote!(false),
721 );
722 *slot = Some(quote!({
723 let __item = Self::#ident(#value);
724 match &__item {
726 Self::#ident(__read) => {
727 let _ = __read;
728 }
729 #[allow(unreachable_patterns)]
730 _ => {}
731 }
732 ::core::result::Result::Ok(::core::option::Option::Some(__item))
733 }));
734 continue;
735 }
736 if short.is_none() && longs.is_empty() {
737 longs.push(kebab_case(&ident.to_string()));
738 }
739 let spelled = short
740 .map(|c| format!("-{c}"))
741 .into_iter()
742 .chain(longs.iter().map(|l| format!("--{l}")));
743 for name in spelled {
744 if let Some(other) = seen.insert(name.clone(), ident.clone()) {
745 return Err(syn::Error::new(
746 variant.span(),
747 format!("`{name}` is already used by `{other}`"),
748 ));
749 }
750 }
751 let choices = match takes {
752 Some(ty) => {
753 let ty = spanned.unwrap_or(ty);
754 quote!(<#ty as ::winnow_args::FromArg>::CHOICES)
755 }
756 None => quote!(&[]),
757 };
758 help_items.push(occurrence_item(
759 variant,
760 short,
761 &longs,
762 takes
763 .is_some()
764 .then(|| value_name.as_deref().unwrap_or("VALUE")),
765 choices,
766 require_equals,
767 default_missing.is_some(),
768 ));
769 let body = match takes {
770 None => quote! {
771 __arg.check_switch()?;
772 ::core::result::Result::Ok(::core::option::Option::Some(Self::#ident))
773 },
774 Some(ty) => {
775 let ty = spanned.unwrap_or(ty);
776 let options = quote! {
777 ::winnow_args::token::ValueOptions {
778 negative_numbers: #negative_numbers,
779 hyphen_values: #hyphen_values,
780 require_equals: #require_equals,
781 keep_equals: #keep_equals,
782 }
783 };
784 let read = match &default_missing {
785 Some(missing) => {
786 let bytes = LitByteStr::new(missing.as_bytes(), Span::call_site());
787 quote! {
788 __arg.read_value_or_with(
789 __input,
790 #options,
791 ::winnow_args::__private::BStr::new(#bytes),
792 )
793 }
794 }
795 None => quote!(__arg.read_value_with(__input, #options)?),
796 };
797 let value = wrap(
798 quote!(__arg.convert::<#ty>(__value)?),
799 quote!(__arg.offset()),
800 quote!(__attached),
801 );
802 let attached = spanned
803 .is_some()
804 .then(|| quote!(let __attached = __arg.has_attached_value(__input);));
805 quote! {
806 #attached
807 let __value = #read;
808 {
809 let __item = Self::#ident(#value);
810 match &__item {
812 Self::#ident(__read) => {
813 let _ = __read;
814 }
815 #[allow(unreachable_patterns)]
816 _ => {}
817 }
818 ::core::result::Result::Ok(::core::option::Option::Some(__item))
819 }
820 }
821 }
822 };
823 if !longs.is_empty() {
824 let pattern = byte_patterns(&longs);
825 long_arms.push(quote!(#pattern => { #body }));
826 if !two_dashes {
827 single_dash.extend(longs.iter().cloned());
828 }
829 }
830 if let Some(c) = short {
831 if prefix {
832 if !c.is_ascii() || takes.is_none() {
833 return Err(syn::Error::new(
834 variant.span(),
835 "`prefix` is for a value-taking variant with an ASCII `short` letter",
836 ));
837 }
838 prefixes.push(c as u8);
839 }
840 let letter = LitChar::new(c, Span::call_site());
841 let takes_value = takes.is_some();
842 letters.push(quote!(#letter => ::core::option::Option::Some(#takes_value),));
843 short_arms.push(quote!(#letter => { #body }));
844 }
845 }
846 let has_positional = word.is_some();
847 let word =
848 word.unwrap_or_else(|| quote!(::core::result::Result::Ok(::core::option::Option::None)));
849 let bundle = bundle.map(|body| {
850 quote! {
851 const BUNDLES: bool = true;
852
853 fn from_bundle(
854 __word: &::winnow_args::token::Word<'_>,
855 ) -> ::core::result::Result<::core::option::Option<Self>, ::winnow_args::Error> {
856 #body
857 }
858 }
859 });
860 let unknown = unknown.map(|body| {
861 quote! {
862 fn from_unknown(
863 __word: &::winnow_args::token::Word<'_>,
864 ) -> ::core::result::Result<::core::option::Option<Self>, ::winnow_args::Error> {
865 #body
866 }
867 }
868 });
869 let is_long = matches_name(&single_dash);
870 let name = &input.ident;
871 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
872 Ok(quote! {
873 impl #impl_generics ::winnow_args::Occurrence for #name #ty_generics #where_clause {
874 const PREFIXES: &'static [u8] = &[#(#prefixes),*];
875
876 const POSITIONAL: bool = #has_positional;
877
878 const ITEMS: &'static [::winnow_args::help::Item] = &[#(#help_items),*];
879
880 fn from_arg<'__i>(
881 __arg: &::winnow_args::Arg<'__i>,
882 __input: &mut ::winnow_args::Argv<'__i>,
883 ) -> ::core::result::Result<::core::option::Option<Self>, ::winnow_args::Error> {
884 let _ = &__input;
885 match __arg {
886 ::winnow_args::Arg::Long(__flag) => match __flag.name {
887 #(#long_arms)*
888 _ => ::core::result::Result::Ok(::core::option::Option::None),
889 },
890 ::winnow_args::Arg::Short(__flag) if !__flag.plus => match __flag.letter {
891 #(#short_arms)*
892 _ => ::core::result::Result::Ok(::core::option::Option::None),
893 },
894 _ => ::core::result::Result::Ok(::core::option::Option::None),
895 }
896 }
897
898 fn from_word(
899 __word: &::winnow_args::token::Word<'_>,
900 ) -> ::core::result::Result<::core::option::Option<Self>, ::winnow_args::Error> {
901 let _ = __word;
902 #word
903 }
904
905 fn is_long(__name: &[u8]) -> bool {
906 #is_long
907 }
908
909 #unknown
910
911 #bundle
912
913 fn short(__letter: char) -> ::core::option::Option<bool> {
914 match __letter {
915 #(#letters)*
916 _ => ::core::option::Option::None,
917 }
918 }
919 }
920 })
921}
922
923fn occurrence_item(
926 variant: &syn::Variant,
927 short: Option<char>,
928 longs: &[String],
929 value_name: Option<&str>,
930 choices: TokenStream2,
931 require_equals: bool,
932 optional_value: bool,
933) -> TokenStream2 {
934 let (doc, long_doc) = docs(&variant.attrs);
935 let (help, long_help) = (text(&doc), text(&long_doc));
936 let positional = short.is_none() && longs.is_empty();
937 let short = opt_char(short);
938 let long = opt_str(longs.first().map(String::as_str));
939 let aliases = longs.iter().skip(1);
940 let value_name = opt_str(value_name);
941 quote!(::winnow_args::help::Item {
942 short: #short,
943 long: #long,
944 aliases: &[#(#aliases),*],
945 negate: ::core::option::Option::None,
946 value_name: #value_name,
947 help: #help,
948 long_help: #long_help,
949 heading: ::core::option::Option::None,
950 hide: false,
951 positional: #positional,
952 required: false,
953 multiple: #positional,
954 trailing: false,
955 default: ::core::option::Option::None,
956 env: ::core::option::Option::None,
957 choices: #choices,
958 require_equals: #require_equals,
959 global: false,
960 more_shorts: &[],
961 plus: ::core::option::Option::None,
962 optional_value: #optional_value,
963 values: 1,
964 more_values: false,
965 })
966}
967
968fn expand_value_enum(input: &DeriveInput) -> syn::Result<TokenStream2> {
970 let Data::Enum(data) = &input.data else {
971 return Err(syn::Error::new(
972 input.span(),
973 "`ValueEnum` can only be derived for enums",
974 ));
975 };
976 let case = rename_all(input)?;
977 let mut names: Vec<(String, &Ident)> = Vec::new();
978 let mut arms = Vec::new();
979 let mut visible = Vec::new();
980 for variant in &data.variants {
981 if !matches!(variant.fields, Fields::Unit) {
982 return Err(syn::Error::new(
983 variant.span(),
984 "a `ValueEnum` variant holds no data",
985 ));
986 }
987 let Variant {
988 names: spellings,
989 hide,
990 ..
991 } = variant_names(variant, &mut names, case)?;
992 let name = LitStr::new(&spellings[0], Span::call_site());
993 if !hide {
994 visible.push(name);
995 }
996 let pattern = byte_patterns(&spellings);
997 let ident = &variant.ident;
998 arms.push(quote!(#pattern => ::core::result::Result::Ok(Self::#ident),));
999 }
1000 let name = &input.ident;
1001 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
1002 Ok(quote! {
1003 impl #impl_generics ::winnow_args::FromArg for #name #ty_generics #where_clause {
1004 const CHOICES: &'static [&'static str] = &[#(#visible),*];
1005
1006 fn from_arg(
1007 __value: &::winnow_args::__private::BStr,
1008 ) -> ::core::result::Result<Self, ::winnow_args::__private::BoxError> {
1009 match &**__value {
1010 #(#arms)*
1011 _ => ::core::result::Result::Err(::std::boxed::Box::new(
1012 ::winnow_args::ChoiceError { choices: Self::CHOICES },
1013 )),
1014 }
1015 }
1016 }
1017 })
1018}
1019
1020fn text(prose: &str) -> TokenStream2 {
1022 if prose.is_empty() {
1023 quote!("")
1024 } else {
1025 quote!(::winnow_args::__text!(#prose))
1026 }
1027}
1028
1029struct Variant {
1031 names: Vec<String>,
1033 shown: usize,
1035 hide: bool,
1036 about: String,
1037}
1038
1039#[derive(Clone, Copy)]
1042enum Case {
1043 Kebab,
1045 Lower,
1047 Upper,
1049 Snake,
1051 Verbatim,
1053}
1054
1055impl Case {
1056 fn apply(self, ident: &str) -> String {
1057 let ident = ident.trim_start_matches("r#");
1058 match self {
1059 Case::Kebab => kebab_case(ident),
1060 Case::Lower => ident.to_lowercase(),
1061 Case::Upper => ident.to_uppercase(),
1062 Case::Snake => kebab_case(ident).replace('-', "_"),
1063 Case::Verbatim => ident.to_owned(),
1064 }
1065 }
1066}
1067
1068fn rename_all(input: &DeriveInput) -> syn::Result<Case> {
1070 let mut case = Case::Kebab;
1071 for attr in input.attrs.iter().filter(|a| is_ours(a)) {
1072 attr.parse_nested_meta(|meta| {
1073 if !meta.path.is_ident("rename_all") {
1074 return Err(meta.error("expected `rename_all`"));
1075 }
1076 let lit = meta.value()?.parse::<LitStr>()?;
1077 case = match lit.value().as_str() {
1078 "kebab-case" => Case::Kebab,
1079 "lowercase" => Case::Lower,
1080 "UPPERCASE" => Case::Upper,
1081 "snake_case" => Case::Snake,
1082 "verbatim" => Case::Verbatim,
1083 _ => {
1084 return Err(syn::Error::new(
1085 lit.span(),
1086 "expected \"kebab-case\", \"lowercase\", \"UPPERCASE\", \"snake_case\" or \"verbatim\"",
1087 ));
1088 }
1089 };
1090 Ok(())
1091 })?;
1092 }
1093 Ok(case)
1094}
1095
1096fn variant_names<'a>(
1098 variant: &'a syn::Variant,
1099 seen: &mut Vec<(String, &'a Ident)>,
1100 case: Case,
1101) -> syn::Result<Variant> {
1102 let (mut name, mut alias, mut hidden, mut hide) = (None, Vec::new(), Vec::new(), false);
1103 let mut help: Option<String> = None;
1104 for attr in variant.attrs.iter().filter(|a| is_ours(a)) {
1105 attr.parse_nested_meta(|meta| {
1106 if meta.path.is_ident("name") {
1107 name = Some(meta.value()?.parse::<LitStr>()?.value());
1108 } else if meta.path.is_ident("alias") {
1109 alias.extend(aliases(&meta)?);
1110 } else if meta.path.is_ident("alias_hidden") {
1111 hidden.extend(aliases(&meta)?);
1112 } else if meta.path.is_ident("hide") {
1113 hide = true;
1114 } else if meta.path.is_ident("help") {
1115 help = Some(meta.value()?.parse::<LitStr>()?.value());
1116 } else if meta.path.is_ident("long_help") {
1117 meta.value()?.parse::<LitStr>()?;
1119 } else {
1120 return Err(meta.error(
1121 "expected `name`, `alias`, `alias_hidden`, `hide`, `help` or `long_help`",
1122 ));
1123 }
1124 Ok(())
1125 })?;
1126 }
1127 let ident = &variant.ident;
1128 let shown = alias.len();
1129 let names: Vec<String> =
1130 std::iter::once(name.unwrap_or_else(|| case.apply(&ident.to_string())))
1131 .chain(alias)
1132 .chain(hidden)
1133 .collect();
1134 for spelling in &names {
1135 if let Some((_, other)) = seen.iter().find(|(n, _)| n == spelling) {
1136 return Err(syn::Error::new(
1137 ident.span(),
1138 format!("`{spelling}` is already used by `{other}`"),
1139 ));
1140 }
1141 seen.push((spelling.clone(), ident));
1142 }
1143 Ok(Variant {
1144 names,
1145 shown,
1146 hide,
1147 about: help
1148 .map(|h| {
1149 h.split("\n\n")
1150 .next()
1151 .unwrap_or_default()
1152 .replace('\n', " ")
1153 })
1154 .unwrap_or_else(|| docs(&variant.attrs).0),
1155 })
1156}
1157
1158fn docs(attrs: &[syn::Attribute]) -> (String, String) {
1160 let lines: Vec<String> = attrs
1161 .iter()
1162 .filter(|a| a.path().is_ident("doc"))
1163 .filter_map(|a| match &a.meta {
1164 syn::Meta::NameValue(nv) => match &nv.value {
1165 syn::Expr::Lit(syn::ExprLit {
1166 lit: syn::Lit::Str(s),
1167 ..
1168 }) => Some(s.value()),
1169 _ => None,
1170 },
1171 _ => None,
1172 })
1173 .map(|line| {
1174 line.strip_prefix(' ')
1175 .unwrap_or(&line)
1176 .trim_end()
1177 .to_owned()
1178 })
1179 .collect();
1180 let long = lines.join("\n").trim().to_owned();
1181 let short = long
1182 .split("\n\n")
1183 .next()
1184 .unwrap_or_default()
1185 .replace('\n', " ");
1186 (short, long)
1187}
1188
1189fn opt_str(s: Option<&str>) -> TokenStream2 {
1191 match s {
1192 Some(s) => quote!(::core::option::Option::Some(#s)),
1193 None => quote!(::core::option::Option::None),
1194 }
1195}
1196
1197fn opt_char(c: Option<char>) -> TokenStream2 {
1199 match c {
1200 Some(c) => quote!(::core::option::Option::Some(#c)),
1201 None => quote!(::core::option::Option::None),
1202 }
1203}
1204
1205fn byte_patterns<S: AsRef<str>>(names: &[S]) -> TokenStream2 {
1207 let literals = names
1208 .iter()
1209 .map(|n| LitByteStr::new(n.as_ref().as_bytes(), Span::call_site()));
1210 quote!(#(#literals)|*)
1211}
1212
1213fn matches_name(names: &[String]) -> TokenStream2 {
1215 if names.is_empty() {
1216 quote!(false)
1217 } else {
1218 let names = byte_patterns(names);
1219 quote!(matches!(__name, #names))
1220 }
1221}
1222
1223fn aliases(meta: &syn::meta::ParseNestedMeta<'_>) -> syn::Result<Vec<String>> {
1225 if meta.input.peek(syn::Token![=]) {
1226 return Ok(vec![meta.value()?.parse::<LitStr>()?.value()]);
1227 }
1228 let content;
1229 syn::parenthesized!(content in meta.input);
1230 let names = syn::punctuated::Punctuated::<LitStr, syn::Token![,]>::parse_terminated(&content)?;
1231 Ok(names.iter().map(LitStr::value).collect())
1232}
1233
1234fn kebab_case(ident: &str) -> String {
1236 let mut out = String::new();
1237 for (i, c) in ident.trim_start_matches("r#").chars().enumerate() {
1238 if c.is_uppercase() {
1239 if i > 0 {
1240 out.push('-');
1241 }
1242 out.extend(c.to_lowercase());
1243 } else {
1244 out.push(c);
1245 }
1246 }
1247 out
1248}
1249
1250fn is_ours(attr: &syn::Attribute) -> bool {
1253 attr.path().is_ident("arg") || attr.path().is_ident("winnow_args")
1254}
1255
1256fn is_bool(ty: &Type) -> bool {
1258 last_segment(ty).is_some_and(|s| s.ident == "bool" && s.arguments.is_none())
1259}
1260
1261fn is_sequence(f: &syn::Field) -> bool {
1263 has_flag(f, "sequence")
1264}
1265
1266fn is_flatten(f: &syn::Field) -> bool {
1268 has_flag(f, "flatten")
1269}
1270
1271fn is_unknown(f: &syn::Field) -> bool {
1273 has_flag(f, "unknown") && !is_sequence(f)
1274}
1275
1276fn is_skipped(f: &syn::Field) -> bool {
1278 has_flag(f, "skip")
1279}
1280
1281fn has_flag(f: &syn::Field, name: &str) -> bool {
1283 f.attrs.iter().filter(|a| is_ours(a)).any(|a| {
1284 let mut found = false;
1285 let _ = a.parse_nested_meta(|meta| {
1286 if meta.path.is_ident(name) {
1287 found = true;
1288 } else if meta.input.peek(syn::Token![=]) {
1289 meta.value()?.parse::<syn::Expr>()?;
1290 } else if meta.input.peek(syn::token::Paren) {
1291 let _content;
1292 syn::parenthesized!(_content in meta.input);
1293 }
1294 Ok(())
1295 });
1296 found
1297 })
1298}
1299
1300fn role_alone(f: &syn::Field) -> syn::Result<()> {
1303 let role = ["skip", "flatten", "sequence", "unknown"]
1304 .into_iter()
1305 .find(|role| has_flag(f, role));
1306 let Some(role) = role else {
1307 return Ok(());
1308 };
1309 for attr in f.attrs.iter().filter(|a| is_ours(a)) {
1310 attr.parse_nested_meta(|meta| {
1311 let alone =
1312 meta.path.is_ident(role) || (role == "sequence" && meta.path.is_ident("unknown"));
1313 if alone {
1314 Ok(())
1315 } else {
1316 Err(meta.error(format!("a `{role}` field takes no other `arg` options")))
1317 }
1318 })?;
1319 }
1320 Ok(())
1321}
1322
1323fn boxed(ty: &Type) -> Option<&Type> {
1325 let last = last_segment(ty)?;
1326 if last.ident == "Box" {
1327 inner(last)
1328 } else {
1329 None
1330 }
1331}
1332
1333enum Kind {
1334 Switch,
1336 Count(Type),
1338 Optional(Type),
1340 Required(Type),
1342 Many(Type),
1344}
1345
1346enum Role {
1347 Flag {
1348 short: Option<char>,
1349 long: Option<String>,
1350 aliases: Vec<String>,
1352 global: bool,
1354 },
1355 Positional {
1356 name: String,
1357 double_dash: DoubleDash,
1358 },
1359 Subcommand,
1360}
1361
1362#[derive(Clone, Copy, PartialEq, Eq)]
1364enum DoubleDash {
1365 Optional,
1367 Required,
1369 Automatic,
1371 Preserve,
1373}
1374
1375struct Field {
1376 ident: Ident,
1377 kind: Kind,
1378 role: Role,
1379 delimiter: Option<u8>,
1381 choices: Option<Vec<String>>,
1383 env: Option<String>,
1385 default: Option<String>,
1387 conflicts: Vec<String>,
1389 overrides: Vec<String>,
1391 requires: Vec<String>,
1393 required: bool,
1395 required_unless: Vec<String>,
1397 group: Option<String>,
1399 default_missing: Option<String>,
1401 help: String,
1403 long_help: String,
1404 heading: Option<String>,
1405 hide: bool,
1406 value_name: Option<String>,
1408 negative_numbers: bool,
1410 hyphen_values: bool,
1412 require_equals: bool,
1414 keep_equals: bool,
1416 negate: Option<String>,
1419 two_dashes: bool,
1421 values: usize,
1423 values_max: Option<usize>,
1426 value_terminator: Option<String>,
1428 default_fn: Option<syn::ExprPath>,
1430 default_note: Option<String>,
1432 default_ifs: Vec<(String, Option<String>, String)>,
1434 stop_flags: bool,
1436 plus: Option<char>,
1438 tristate: bool,
1441 keywords: bool,
1444 short_aliases: Vec<char>,
1446 prefix: bool,
1449}
1450
1451impl Field {
1452 fn help_item(&self) -> TokenStream2 {
1454 let short = opt_char(self.short());
1455 let long = opt_str(self.longs().first().copied());
1456 let aliases = self.longs().into_iter().skip(1);
1457 let negate = opt_str(self.negate.as_deref());
1458 let require_equals = self.require_equals;
1459 let global = self.is_global();
1460 let positional = self.is_positional();
1461 let trailing = self.double_dash() == DoubleDash::Required;
1462 let display = self.display();
1463 let value_name = if positional {
1464 opt_str(Some(&display))
1465 } else {
1466 opt_str(self.value_name.as_deref())
1467 };
1468 let (help, long_help) = (text(&self.help), text(&self.long_help));
1469 let heading = opt_str(self.heading.as_deref());
1470 let hide = self.hide;
1471 let more_shorts = self.short_aliases.iter();
1472 let plus = opt_char(self.plus);
1473 let optional_value = self.default_missing.is_some();
1474 let values = self.values;
1475 let more_values = self.values_max.is_some();
1476 let required = matches!(self.kind, Kind::Required(_))
1477 && self.default.is_none()
1478 && self.default_fn.is_none()
1479 && self.env.is_none()
1480 && !self.keywords
1481 || self.required;
1482 let multiple = matches!(self.kind, Kind::Many(_) | Kind::Count(_));
1483 let default = opt_str(self.default.as_deref().or(self.default_note.as_deref()));
1484 let env = opt_str(self.env.as_deref());
1485 let choices = match (&self.choices, &self.kind, &self.role) {
1487 (Some(choices), _, _) => quote!(&[#(#choices),*]),
1488 (None, _, Role::Subcommand) | (None, Kind::Switch | Kind::Count(_), _) => quote!(&[]),
1489 (None, _, _) if self.keywords || self.tristate => quote!(&[]),
1490 (None, Kind::Optional(ty) | Kind::Required(ty) | Kind::Many(ty), _) => {
1491 quote!(<#ty as ::winnow_args::FromArg>::CHOICES)
1492 }
1493 };
1494 quote! {
1495 ::winnow_args::help::Item {
1496 short: #short,
1497 long: #long,
1498 aliases: &[#(#aliases),*],
1499 negate: #negate,
1500 value_name: #value_name,
1501 help: #help,
1502 long_help: #long_help,
1503 heading: #heading,
1504 hide: #hide,
1505 positional: #positional,
1506 required: #required,
1507 multiple: #multiple,
1508 trailing: #trailing,
1509 default: #default,
1510 env: #env,
1511 choices: #choices,
1512 require_equals: #require_equals,
1513 global: #global,
1514 more_shorts: &[#(#more_shorts),*],
1515 plus: #plus,
1516 optional_value: #optional_value,
1517 values: #values,
1518 more_values: #more_values,
1519 }
1520 }
1521 }
1522
1523 fn value_options(&self) -> TokenStream2 {
1525 let (negative_numbers, hyphen_values, require_equals, keep_equals) = (
1526 self.negative_numbers,
1527 self.hyphen_values,
1528 self.require_equals,
1529 self.keep_equals,
1530 );
1531 quote! {
1532 __wa::ValueOptions {
1533 negative_numbers: #negative_numbers,
1534 hyphen_values: #hyphen_values,
1535 require_equals: #require_equals,
1536 keep_equals: #keep_equals,
1537 }
1538 }
1539 }
1540
1541 fn read(&self) -> TokenStream2 {
1543 let (negative_numbers, hyphen_values, require_equals, keep_equals) = (
1544 self.negative_numbers,
1545 self.hyphen_values,
1546 self.require_equals,
1547 self.keep_equals,
1548 );
1549 let options = self.value_options();
1550 match &self.default_missing {
1551 None if !negative_numbers && !hyphen_values && !require_equals && !keep_equals => {
1552 quote!(__arg.read_value(__input)?)
1553 }
1554 None => quote!(__arg.read_value_with(__input, #options)?),
1555 Some(missing) => {
1556 let bytes = LitByteStr::new(missing.as_bytes(), Span::call_site());
1557 quote!(__arg.read_value_or_with(__input, #options, __wa::BStr::new(#bytes)))
1558 }
1559 }
1560 }
1561
1562 fn check(
1564 &self,
1565 value: TokenStream2,
1566 error: impl FnOnce(TokenStream2) -> TokenStream2,
1567 ) -> TokenStream2 {
1568 let Some(choices) = &self.choices else {
1569 return quote!();
1570 };
1571 let pattern = byte_patterns(choices);
1572 let listed = choices.iter().map(|c| LitStr::new(c, Span::call_site()));
1573 let cause = quote!(::winnow_args::ChoiceError { choices: &[#(#listed),*] });
1574 let error = error(cause);
1575 quote! {
1576 if !matches!(&**#value, #pattern) {
1577 return ::core::result::Result::Err(#error);
1578 }
1579 }
1580 }
1581
1582 fn flag_value(&self, ty: &Type, value: TokenStream2) -> TokenStream2 {
1584 let check = self.check(value.clone(), |cause| {
1585 quote!(__wa::Error::invalid_value(__arg.offset(), __arg.spelling(), #value, #cause))
1586 });
1587 quote!({ #check __arg.convert::<#ty>(#value)? })
1588 }
1589
1590 fn source_value(&self, ty: &Type, value: TokenStream2, source: &str) -> TokenStream2 {
1593 let source = LitStr::new(source, Span::call_site());
1594 let check = self.check(
1595 value.clone(),
1596 |cause| quote!(__wa::Error::invalid_value(__input.offset(), #source, #value, #cause)),
1597 );
1598 quote!({
1599 #check
1600 <#ty as __wa::FromArg>::from_arg(#value).map_err(|cause| {
1601 __wa::Error::invalid_value(__input.offset(), #source, #value, cause)
1602 })?
1603 })
1604 }
1605
1606 fn has(&self) -> TokenStream2 {
1609 let slot = slot(&self.ident);
1610 if self.keywords {
1611 return quote!((!#slot.is_empty()));
1612 }
1613 match &self.kind {
1614 Kind::Switch if self.negate.is_some() => {
1615 quote!((#slot == ::core::option::Option::Some(true)))
1616 }
1617 Kind::Switch => quote!(#slot),
1618 Kind::Count(_) => quote!((#slot != 0)),
1619 Kind::Optional(_) | Kind::Required(_) => quote!(#slot.is_some()),
1620 Kind::Many(_) => quote!((!#slot.is_empty())),
1621 }
1622 }
1623
1624 fn assign(&self, value: TokenStream2, source: &str) -> TokenStream2 {
1627 let slot = slot(&self.ident);
1628 match &self.kind {
1629 Kind::Optional(ty) | Kind::Required(ty) => {
1630 let value = self.source_value(ty, value, source);
1631 quote!(#slot = ::core::option::Option::Some(#value);)
1632 }
1633 Kind::Many(ty) => match self.delimiter {
1634 None => {
1635 let value = self.source_value(ty, value, source);
1636 quote!(#slot.push(#value);)
1637 }
1638 Some(d) => {
1639 let piece = self.source_value(ty, quote!(__piece), source);
1640 quote!(for __piece in __wa::split(#value, #d) { #slot.push(#piece); })
1641 }
1642 },
1643 Kind::Switch | Kind::Count(_) => unreachable!("a switch or a count takes no value"),
1644 }
1645 }
1646
1647 fn unset(&self, displaced: bool) -> TokenStream2 {
1649 let slot = slot(&self.ident);
1650 if self.negate.is_some() {
1651 return quote!(#slot.is_none());
1652 }
1653 let has = self.has();
1654 let not_displaced = displaced.then(|| {
1655 let displaced = displaced_flag(&self.ident);
1656 quote!(&& !#displaced)
1657 });
1658 quote!(!#has #not_displaced)
1659 }
1660
1661 fn fallback(&self, env: bool, displaced: bool) -> TokenStream2 {
1664 let slot = slot(&self.ident);
1665 if self.negate.is_some() {
1666 return self.negatable_fallback(env);
1667 }
1668 let unset = self.unset(displaced);
1669 let assign = |value: TokenStream2, source: &str| self.assign(value, source);
1670 if !env {
1671 if let Some(function) = &self.default_fn {
1672 let set = match &self.kind {
1673 Kind::Many(_) => quote!(#slot = #function();),
1674 _ => quote!(#slot = ::core::option::Option::Some(#function());),
1675 };
1676 return quote!(if #unset { #set });
1677 }
1678 return self
1679 .default
1680 .as_ref()
1681 .map(|value| {
1682 let bytes = LitByteStr::new(value.as_bytes(), Span::call_site());
1683 let assign = assign(quote!(__wa::BStr::new(#bytes)), &self.display());
1684 quote!(if #unset { #assign })
1685 })
1686 .unwrap_or_default();
1687 }
1688 self.env
1689 .as_ref()
1690 .map(|var| {
1691 let name = LitStr::new(var, Span::call_site());
1692 let apply = match &self.kind {
1693 Kind::Switch => quote!(#slot = __wa::env::truthy(&__raw);),
1694 Kind::Count(ty) => quote! {
1695 if let ::core::option::Option::Some(::core::result::Result::Ok(__n)) =
1696 __raw.to_str().map(str::parse::<#ty>)
1697 {
1698 #slot = __n;
1699 }
1700 },
1701 _ => {
1702 let assign = assign(quote!(__value), &format!("${var}"));
1703 quote! {
1704 let __value = __wa::BStr::new(__raw.as_encoded_bytes());
1705 #assign
1706 }
1707 }
1708 };
1709 quote! {
1710 if #unset {
1711 if let ::core::option::Option::Some(__raw) = __wa::env::var(#name) {
1712 #apply
1713 }
1714 }
1715 }
1716 })
1717 .unwrap_or_default()
1718 }
1719
1720 fn negatable_fallback(&self, env: bool) -> TokenStream2 {
1722 let slot = slot(&self.ident);
1723 let value = if env {
1724 let Some(var) = &self.env else {
1725 return quote!();
1726 };
1727 let name = LitStr::new(var, Span::call_site());
1728 quote! {
1729 if let ::core::option::Option::Some(__raw) = __wa::env::var(#name) {
1730 #slot = ::core::option::Option::Some(__wa::env::truthy(&__raw));
1731 }
1732 }
1733 } else {
1734 match (&self.default_fn, &self.default) {
1735 (Some(function), _) => quote!(#slot = ::core::option::Option::Some(#function());),
1736 (None, Some(default)) => {
1737 let default = default == "true";
1738 quote!(#slot = ::core::option::Option::Some(#default);)
1739 }
1740 (None, None) => return quote!(),
1741 }
1742 };
1743 quote!(if #slot.is_none() { #value })
1744 }
1745
1746 fn word_value(&self, ty: &Type, word: TokenStream2, display: &LitStr) -> TokenStream2 {
1748 let check = self.check(
1749 quote!(#word.value),
1750 |cause| quote!(__wa::Error::invalid_value(#word.offset, #display, #word.value, #cause)),
1751 );
1752 quote!({ #check #word.convert::<#ty>(#display)? })
1753 }
1754
1755 fn display(&self) -> String {
1757 match &self.role {
1758 Role::Flag { long: Some(l), .. } => format!("--{l}"),
1759 Role::Flag { short: Some(c), .. } => format!("-{c}"),
1760 Role::Flag { .. } => match self.plus {
1761 Some(c) => format!("+{c}"),
1762 None => unreachable!("every flag has a name"),
1763 },
1764 Role::Positional { name, .. } => name.clone(),
1765 Role::Subcommand => "<COMMAND>".to_owned(),
1766 }
1767 }
1768
1769 fn short_pattern(&self) -> Option<TokenStream2> {
1771 let first = self.short()?;
1772 let letters = std::iter::once(first)
1773 .chain(self.short_aliases.iter().copied())
1774 .map(|c| LitChar::new(c, Span::call_site()));
1775 Some(quote!(#(#letters)|*))
1776 }
1777
1778 fn shorts(&self) -> Vec<char> {
1780 self.short()
1781 .into_iter()
1782 .chain(self.short_aliases.iter().copied())
1783 .collect()
1784 }
1785
1786 fn short(&self) -> Option<char> {
1787 match self.role {
1788 Role::Flag { short, .. } => short,
1789 Role::Positional { .. } | Role::Subcommand => None,
1790 }
1791 }
1792
1793 fn longs(&self) -> Vec<&str> {
1795 match &self.role {
1796 Role::Flag { long, aliases, .. } => {
1797 long.iter().chain(aliases).map(String::as_str).collect()
1798 }
1799 Role::Positional { .. } | Role::Subcommand => Vec::new(),
1800 }
1801 }
1802
1803 fn long_pattern(&self) -> Option<TokenStream2> {
1805 let longs = self.longs();
1806 (!longs.is_empty()).then(|| byte_patterns(&longs))
1807 }
1808
1809 fn takes_value(&self) -> bool {
1811 !self.tristate && !matches!(self.kind, Kind::Switch | Kind::Count(_))
1812 }
1813
1814 fn is_positional(&self) -> bool {
1815 matches!(self.role, Role::Positional { .. })
1816 }
1817
1818 fn is_global(&self) -> bool {
1819 matches!(self.role, Role::Flag { global: true, .. })
1820 }
1821
1822 fn double_dash(&self) -> DoubleDash {
1824 match self.role {
1825 Role::Positional { double_dash, .. } => double_dash,
1826 _ => DoubleDash::Optional,
1827 }
1828 }
1829}
1830
1831fn expand(input: &DeriveInput) -> syn::Result<TokenStream2> {
1832 let Data::Struct(data) = &input.data else {
1833 return Err(syn::Error::new(
1834 input.span(),
1835 "`Args` can only be derived for structs",
1836 ));
1837 };
1838 let Fields::Named(named) = &data.fields else {
1839 return Err(syn::Error::new(
1840 data.fields.span(),
1841 "`Args` needs a struct with named fields",
1842 ));
1843 };
1844 for f in &named.named {
1845 role_alone(f)?;
1846 }
1847 let fields = named
1848 .named
1849 .iter()
1850 .filter(|f| !is_skipped(f) && !is_sequence(f) && !is_unknown(f) && !is_flatten(f))
1851 .map(field)
1852 .collect::<syn::Result<Vec<_>>>()?;
1853 let skipped: Vec<&Ident> = named
1854 .named
1855 .iter()
1856 .filter(|f| is_skipped(f) && !is_sequence(f) && !is_unknown(f) && !is_flatten(f))
1857 .filter_map(|f| f.ident.as_ref())
1858 .collect();
1859 let sequences: Vec<&syn::Field> = named.named.iter().filter(|f| is_sequence(f)).collect();
1862 if let Some(extra) = sequences.get(1) {
1863 return Err(syn::Error::new(
1864 extra.span(),
1865 "a struct has at most one `sequence` field",
1866 ));
1867 }
1868 let sequence_unknown = sequences.first().is_some_and(|f| has_flag(f, "unknown"));
1871 let sequence = vec_field(
1872 sequences.first().copied(),
1873 "a `sequence` field is a `Vec<T>` of an `Occurrence` enum",
1874 )?;
1875 let sequence_ty = sequence.as_ref().map(|(_, ty)| ty);
1876 let unknowns: Vec<&syn::Field> = named.named.iter().filter(|f| is_unknown(f)).collect();
1878 if let Some(extra) = unknowns.get(1) {
1879 return Err(syn::Error::new(
1880 extra.span(),
1881 "a struct has at most one `unknown` field",
1882 ));
1883 }
1884 let unknown = vec_field(
1885 unknowns.first().copied(),
1886 "an `unknown` field is a `Vec<T>`",
1887 )?;
1888 let flattens: Vec<(Ident, Type)> = named
1890 .named
1891 .iter()
1892 .filter(|f| is_flatten(f))
1893 .map(|f| (f.ident.clone().expect("named field"), f.ty.clone()))
1894 .collect();
1895 if !flattens.is_empty() && !input.generics.params.is_empty() {
1896 return Err(syn::Error::new(
1897 input.generics.span(),
1898 "`flatten` in a generic struct is not supported yet",
1899 ));
1900 }
1901 check_duplicates(&fields)?;
1902 let positionals: Vec<&Field> = fields
1905 .iter()
1906 .filter(|f| f.is_positional() && f.double_dash() != DoubleDash::Required)
1907 .collect();
1908 check_positional_order(&positionals)?;
1909 let trailing: Vec<&Field> = fields
1910 .iter()
1911 .filter(|f| f.double_dash() == DoubleDash::Required)
1912 .collect();
1913 if let Some(extra) = trailing.get(1) {
1914 return Err(syn::Error::new(
1915 extra.ident.span(),
1916 "a struct has at most one `double_dash = \"required\"` positional",
1917 ));
1918 }
1919 let trailing = trailing.first().copied();
1920 let preserving: Vec<usize> = positionals
1922 .iter()
1923 .enumerate()
1924 .filter(|(_, f)| f.double_dash() == DoubleDash::Preserve)
1925 .map(|(i, _)| i)
1926 .collect();
1927 if let (Some(t), false) = (trailing, preserving.is_empty()) {
1928 return Err(syn::Error::new(
1929 t.ident.span(),
1930 "a `double_dash = \"preserve\"` positional keeps the `--` this one waits for",
1931 ));
1932 }
1933 let StructOptions {
1934 groups,
1935 restart_token,
1936 default_subcommand,
1937 arg_required_else_help,
1938 name: program_name,
1939 version,
1940 about,
1941 long_about,
1942 after_help,
1943 after_long_help,
1944 disable_help_flag,
1945 disable_help_short,
1946 disable_version_flag,
1947 disable_help_subcommand,
1948 unknown_flags_value,
1949 long_only,
1950 plus_options,
1951 } = struct_options(input)?;
1952 let (doc_about, doc_long_about) = docs(&input.attrs);
1953 let about = about.unwrap_or(doc_about);
1954 let long_about = long_about.unwrap_or(doc_long_about);
1955 let (about, long_about) = (text(&about), text(&long_about));
1956 let (after_help, after_long_help) = (text(&after_help), text(&after_long_help));
1957 let rules = Rules::new(&fields, &groups)?;
1958
1959 let name = &input.ident;
1960 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
1961
1962 let slot_parts: Vec<(Ident, TokenStream2, TokenStream2)> = fields
1964 .iter()
1965 .map(|f| {
1966 let none = quote!(::core::option::Option::None);
1967 let new = quote!(::std::vec::Vec::new());
1968 let (ty, init) = match &f.kind {
1969 _ if f.keywords => (quote!(::std::vec::Vec<&__wa::BStr>), new),
1971 Kind::Switch if f.negate.is_some() => (quote!(::core::option::Option<bool>), none),
1972 Kind::Switch => (quote!(bool), quote!(false)),
1973 Kind::Count(ty) => (quote!(#ty), quote!(0)),
1974 Kind::Optional(ty) | Kind::Required(ty) => {
1975 (quote!(::core::option::Option<#ty>), none)
1976 }
1977 Kind::Many(ty) => (quote!(::std::vec::Vec<#ty>), new),
1978 };
1979 (slot(&f.ident), ty, init)
1980 })
1981 .collect();
1982 let slots = slot_parts
1983 .iter()
1984 .map(|(ident, ty, init)| quote!(let mut #ident: #ty = #init;));
1985
1986 let store = |f: &Field| {
1988 let ident = slot(&f.ident);
1989 let displace = rules.displace(&fields, f);
1990 let stored = match &f.kind {
1991 _ if f.keywords => {
1992 let read = f.read();
1993 quote! {
1994 let __value = #read;
1995 #ident.push(__value);
1996 }
1997 }
1998 _ if f.tristate || f.negate.is_some() => quote! {
1999 __arg.check_switch()?;
2000 #ident = ::core::option::Option::Some(true);
2001 },
2002 Kind::Switch => quote! {
2003 __arg.check_switch()?;
2004 #ident = true;
2005 },
2006 Kind::Count(_) => quote! {
2007 __arg.check_switch()?;
2008 #ident = #ident.saturating_add(1);
2009 },
2010 Kind::Optional(ty) | Kind::Required(ty) => {
2011 let value = f.flag_value(ty, quote!(__value));
2012 let read = f.read();
2013 quote! {
2014 let __value = #read;
2015 #ident = ::core::option::Option::Some(#value);
2016 }
2017 }
2018 Kind::Many(ty) => match f.delimiter {
2019 None => {
2020 let value = f.flag_value(ty, quote!(__value));
2021 let read = f.read();
2022 let more = match f.values_max {
2023 None => (f.values > 1).then(|| {
2025 let more = f.values - 1;
2026 quote! {
2027 for _ in 0..#more {
2028 let __value = __arg.read_next(__input)?;
2029 #ident.push(#value);
2030 }
2031 }
2032 }),
2033 Some(max) => {
2035 let (min, options) = (f.values, f.value_options());
2036 let terminator = match &f.value_terminator {
2037 Some(word) => {
2038 let word = LitByteStr::new(word.as_bytes(), Span::call_site());
2039 quote!(::core::option::Option::Some(__wa::BStr::new(#word)))
2040 }
2041 None => quote!(::core::option::Option::None),
2042 };
2043 Some(quote! {
2044 let mut __taken = 1usize;
2045 while __taken < #max {
2046 let ::core::option::Option::Some(__value) =
2047 __arg.read_more(__input, #options, #terminator)
2048 else {
2049 break;
2050 };
2051 #ident.push(#value);
2052 __taken += 1;
2053 }
2054 __arg.check_values(__taken, #min)?;
2055 })
2056 }
2057 };
2058 quote! {
2059 let __value = #read;
2060 #ident.push(#value);
2061 #more
2062 }
2063 }
2064 Some(d) => {
2065 let value = f.flag_value(ty, quote!(__piece));
2066 let read = f.read();
2067 quote! {
2068 for __piece in __wa::split(#read, #d) {
2069 #ident.push(#value);
2070 }
2071 }
2072 }
2073 },
2074 };
2075 quote!(#stored #displace)
2076 };
2077
2078 let negated = |f: &Field| {
2079 let no = LitByteStr::new(f.negate.as_ref()?.as_bytes(), Span::call_site());
2080 let ident = slot(&f.ident);
2081 let displace = rules.displace(&fields, f);
2082 Some((
2083 quote!(#no),
2084 quote! {
2085 __arg.check_switch()?;
2086 #ident = ::core::option::Option::Some(false);
2087 #displace
2088 },
2089 ))
2090 };
2091 let plus = |f: &Field| {
2093 let letter = LitChar::new(f.plus?, Span::call_site());
2094 let body = if f.tristate {
2095 let ident = slot(&f.ident);
2096 let displace = rules.displace(&fields, f);
2097 quote!(#ident = ::core::option::Option::Some(false); #displace)
2098 } else {
2099 store(f)
2100 };
2101 Some((quote!(#letter), body))
2102 };
2103 type Part<'a> = &'a dyn Fn(&Field) -> Option<(TokenStream2, TokenStream2)>;
2106 let arms = |keep: &dyn Fn(&Field) -> bool, tail: TokenStream2| {
2107 let of = |part: Part<'_>| -> Vec<TokenStream2> {
2108 fields
2109 .iter()
2110 .filter(|f| keep(f))
2111 .filter_map(part)
2112 .map(|(pattern, body)| quote!(#pattern => { #body #tail }))
2113 .collect()
2114 };
2115 let mut long = of(&|f| Some((f.long_pattern()?, store(f))));
2116 long.extend(of(&negated));
2117 let short = of(&|f| Some((f.short_pattern()?, store(f))));
2118 (long, short, of(&plus))
2119 };
2120 let (long_arms, short_arms, plus_arms) = arms(&|_| true, quote!());
2121
2122 let unexpected = quote!(return ::core::result::Result::Err(__arg.unexpected()));
2123 let subcommands: Vec<&Field> = fields
2124 .iter()
2125 .filter(|f| matches!(f.role, Role::Subcommand))
2126 .collect();
2127 if let Some(extra) = subcommands.get(1) {
2128 return Err(syn::Error::new(
2129 extra.ident.span(),
2130 "a struct has at most one subcommand field",
2131 ));
2132 }
2133 let subcommand = subcommands.first();
2134 let track_filled = subcommand.is_some() && !positionals.is_empty();
2136 let filled = track_filled.then(|| quote!(__filled = true;));
2137
2138 let overflow = match trailing {
2141 Some(t) => {
2142 let display = LitStr::new(&t.display(), Span::call_site());
2143 quote! {
2144 return ::core::result::Result::Err(
2145 __wa::Error::requires_double_dash(__word.offset, #display),
2146 )
2147 }
2148 }
2149 None => unexpected.clone(),
2150 };
2151 let positional_match = if positionals.is_empty() {
2152 quote!({ #overflow; })
2153 } else {
2154 let arms = positionals.iter().enumerate().map(|(i, f)| {
2155 let ident = slot(&f.ident);
2156 let display = LitStr::new(&f.display(), Span::call_site());
2157 let stop = (f.double_dash() == DoubleDash::Automatic || f.stop_flags)
2158 .then(|| quote!(__input.stop_flags();));
2159 match &f.kind {
2160 Kind::Optional(ty) | Kind::Required(ty) => {
2161 let value = f.word_value(ty, quote!(__word), &display);
2162 quote! {
2163 #i => {
2164 #ident = ::core::option::Option::Some(#value);
2165 __position += 1;
2166 #filled
2167 #stop
2168 }
2169 }
2170 }
2171 Kind::Many(ty) => {
2172 let push = match f.delimiter {
2173 None => {
2174 let value = f.word_value(ty, quote!(__word), &display);
2175 quote! {
2178 if #ident.capacity() == 0 {
2179 #ident.reserve_exact(__input.words_left() + 1);
2180 }
2181 #ident.push(#value);
2182 }
2183 }
2184 Some(d) => {
2185 let value = f.word_value(ty, quote!(__piece), &display);
2186 quote! {
2187 for __piece in __word.split(#d) {
2188 #ident.push(#value);
2189 }
2190 }
2191 }
2192 };
2193 quote! {
2194 #i => {
2195 #push
2196 #filled
2197 #stop
2198 }
2199 }
2200 }
2201 Kind::Switch | Kind::Count(_) => {
2202 unreachable!("rejected for positionals in `field`")
2203 }
2204 }
2205 });
2206 quote! {
2207 match __position {
2208 #(#arms)*
2209 _ => { #overflow; }
2210 }
2211 }
2212 };
2213 let positional_match = match trailing {
2214 None => positional_match,
2215 Some(t) => {
2216 let ident = slot(&t.ident);
2217 let display = LitStr::new(&t.display(), Span::call_site());
2218 let store = match &t.kind {
2219 Kind::Many(ty) => match t.delimiter {
2220 None => {
2221 let value = t.word_value(ty, quote!(__word), &display);
2222 quote!(#ident.push(#value);)
2223 }
2224 Some(d) => {
2225 let value = t.word_value(ty, quote!(__piece), &display);
2226 quote!(for __piece in __word.split(#d) { #ident.push(#value); })
2227 }
2228 },
2229 Kind::Optional(ty) | Kind::Required(ty) => {
2230 let value = t.word_value(ty, quote!(__word), &display);
2231 quote! {
2232 if #ident.is_some() { #unexpected; }
2233 #ident = ::core::option::Option::Some(#value);
2234 }
2235 }
2236 Kind::Switch | Kind::Count(_) => {
2237 unreachable!("rejected for positionals in `field`")
2238 }
2239 };
2240 quote! {
2241 if __word.after_separator {
2242 #store
2243 } else {
2244 #positional_match
2245 }
2246 }
2247 }
2248 };
2249 let route = subcommand.map(|f| {
2250 let ident = slot(&f.ident);
2251 let ty = subcommand_type(f);
2252 let not_filled = track_filled.then(|| quote!(!__filled &&));
2253 let (global_longs, global_shorts, global_plus) = arms(
2254 &Field::is_global,
2255 quote!(return ::core::result::Result::Ok(true);),
2256 );
2257 let (inherit, handler) = if fields.iter().any(Field::is_global) {
2259 let shorts = fields.iter().filter(|f| f.is_global()).flat_map(|f| {
2260 let takes_value = f.takes_value();
2261 f.shorts().into_iter().map(move |c| {
2262 let c = LitChar::new(c, Span::call_site());
2263 quote!((#c, #takes_value))
2264 })
2265 });
2266 let setup = quote! {
2267 let mut __inherit = __wa::inherit(&[#(#shorts),*], __globals, |__arg, __input, __parent| {
2268 match __arg {
2269 __wa::Arg::Long(__flag) => match __flag.name {
2270 #(#global_longs)*
2271 _ => {}
2272 },
2273 __wa::Arg::Short(__flag) if __flag.plus => match __flag.letter {
2274 #(#global_plus)*
2275 _ => {}
2276 },
2277 __wa::Arg::Short(__flag) => match __flag.letter {
2278 #(#global_shorts)*
2279 _ => {}
2280 },
2281 _ => {}
2282 }
2283 __parent.bind(__arg, __input)
2284 });
2285 };
2286 (setup, quote!(&mut __inherit))
2287 } else {
2288 (quote!(), quote!(&mut *__globals))
2289 };
2290 let help_word = (!disable_help_subcommand).then(|| {
2292 quote! {
2293 if &**__word.value == b"help" {
2294 return ::core::result::Result::Err(
2295 __wa::help_word(<Self as ::winnow_args::Args>::HELP, __input),
2296 );
2297 }
2298 }
2299 });
2300 let fallback = default_subcommand.as_ref().map(|name| {
2302 let name = LitByteStr::new(name.as_bytes(), Span::call_site());
2303 quote! {
2304 let __sub = {
2305 #inherit
2306 *__input = __start;
2307 <#ty as __wa::Subcommand>::parse_subcommand(#name, __input, #handler)?
2308 };
2309 #ident = ::core::option::Option::Some(__sub);
2310 break;
2311 }
2312 });
2313 quote! {
2314 if #not_filled !__word.after_separator {
2315 if <#ty as __wa::Subcommand>::has(&**__word.value) {
2316 let __sub = {
2317 #inherit
2318 <#ty as __wa::Subcommand>::parse_subcommand(
2319 &**__word.value, __input, #handler,
2320 )?
2321 };
2322 #ident = ::core::option::Option::Some(__sub);
2323 break;
2324 }
2325 #help_word
2326 #fallback
2327 }
2328 }
2329 });
2330 let restart = restart_token.map(|token| {
2332 let token = LitByteStr::new(token.as_bytes(), Span::call_site());
2333 let clears = fields.iter().filter(|f| f.is_positional()).map(|f| {
2334 let slot = slot(&f.ident);
2335 match f.kind {
2336 Kind::Many(_) => quote!(#slot.clear();),
2337 _ => quote!(#slot = ::core::option::Option::None;),
2338 }
2339 });
2340 let position = (!positionals.is_empty()).then(|| quote!(__position = 0;));
2341 let filled = track_filled.then(|| quote!(__filled = false;));
2342 quote! {
2343 if &**__word.value == #token {
2344 #(#clears)*
2345 #position
2346 #filled
2347 __input.resume_flags();
2348 continue;
2349 }
2350 }
2351 });
2352 let sequence_word = sequence_ty.map(|ty| {
2353 quote! {
2354 if let ::core::option::Option::Some(__item) =
2355 <#ty as ::winnow_args::Occurrence>::from_word(&__word)?
2356 {
2357 __sequence.push(__item);
2358 continue;
2359 }
2360 }
2361 });
2362 let word_arm = quote! {
2363 __wa::Arg::Word(__word) => {
2364 #restart
2365 #route
2366 #sequence_word
2367 #positional_match
2368 }
2369 };
2370 let separator_arm = if preserving.is_empty() {
2371 quote!(__wa::Arg::Separator { .. } => {})
2372 } else {
2373 quote! {
2374 __wa::Arg::Separator { offset: __offset } => {
2375 if matches!(__position, #(#preserving)|*) {
2376 __input.resume_flags();
2377 let __word = __wa::Word {
2378 value: __wa::BStr::new(b"--"),
2379 offset: __offset,
2380 after_separator: false,
2381 };
2382 #positional_match
2383 }
2384 }
2385 }
2386 };
2387 let position = (!positionals.is_empty()).then(|| quote!(let mut __position: usize = 0;));
2388 let filled_slot = track_filled.then(|| quote!(let mut __filled = false;));
2389
2390 let opted: Vec<usize> = positionals
2393 .iter()
2394 .enumerate()
2395 .filter(|(_, f)| f.negative_numbers)
2396 .map(|(i, _)| i)
2397 .collect();
2398 let (unknown_slot, unknown_build) = match &unknown {
2399 Some((ident, ty)) => (
2400 quote!(let mut __unknown: ::std::vec::Vec<#ty> = ::std::vec::Vec::new();),
2401 quote!(#ident: __unknown,),
2402 ),
2403 None => (quote!(), quote!()),
2404 };
2405 let (sequence_slot, sequence_arg, sequence_build) = match &sequence {
2406 Some((ident, ty)) => (
2407 quote! {
2409 let mut __sequence: ::std::vec::Vec<#ty> =
2410 ::std::vec::Vec::with_capacity(__input.words_left());
2411 },
2412 quote! {
2413 if let ::core::option::Option::Some(__item) =
2414 <#ty as ::winnow_args::Occurrence>::from_arg(&__arg, __input)?
2415 {
2416 __sequence.push(__item);
2417 } else
2418 },
2419 quote!(#ident: __sequence,),
2420 ),
2421 None => (quote!(), quote!(), quote!()),
2422 };
2423 let bundle_start = sequence_ty.map(|_| {
2427 quote! {
2428 let __bundle_word = __input.front();
2429 let __bundle_start = __input.mode() == ::winnow_args::stream::Mode::Word;
2430 let __bundle_at = __sequence.len();
2431 }
2432 });
2433 let bundle_end = sequence_ty.map(|ty| {
2434 quote! {
2435 if <#ty as ::winnow_args::Occurrence>::BUNDLES
2436 && __bundle_start
2437 && matches!(__arg, __wa::Arg::Short(__flag) if !__flag.plus)
2438 && __input.mode() == ::winnow_args::stream::Mode::Bundle
2439 {
2440 let __word = __wa::Word {
2441 value: __wa::BStr::new(__bundle_word),
2442 offset: __arg.offset(),
2443 after_separator: false,
2444 };
2445 if let ::core::option::Option::Some(__bundle) =
2446 <#ty as ::winnow_args::Occurrence>::from_bundle(&__word)?
2447 {
2448 __sequence.insert(__bundle_at, __bundle);
2449 }
2450 }
2451 }
2452 });
2453 let flat_slot = |ident: &Ident| format_ident!("__flat_{}", ident);
2456 let flatten_slots = flattens.iter().map(|(ident, ty)| {
2457 let slot = flat_slot(ident);
2458 quote! {
2459 let mut #slot = <<#ty as __wa::Flatten>::Slots as ::core::default::Default>::default();
2460 }
2461 });
2462 let flatten_arg: TokenStream2 = flattens
2463 .iter()
2464 .map(|(ident, ty)| {
2465 let slot = flat_slot(ident);
2466 quote!(if <#ty as __wa::Flatten>::bind(&mut #slot, &__arg, __input)? {} else)
2467 })
2468 .collect();
2469 let flatten_build = flattens.iter().map(|(ident, ty)| {
2470 let slot = flat_slot(ident);
2471 quote!(#ident: <#ty as __wa::Flatten>::finish(#slot, __input)?,)
2472 });
2473 let flatten_shorts: Vec<TokenStream2> = flattens
2474 .iter()
2475 .map(|(_, ty)| {
2476 quote! {
2477 __c if <#ty as __wa::Flatten>::short(__c).is_some() => {
2478 <#ty as __wa::Flatten>::short(__c)
2479 }
2480 }
2481 })
2482 .collect();
2483 let flatten_longs: Vec<TokenStream2> = flattens
2484 .iter()
2485 .map(|(_, ty)| quote!(|| <#ty as __wa::Flatten>::is_long(__name)))
2486 .collect();
2487 let own_shorts: Vec<TokenStream2> = fields
2489 .iter()
2490 .filter_map(|f| {
2491 let letters = f.short_pattern()?;
2492 let takes_value = f.takes_value();
2493 Some(quote!(#letters => ::core::option::Option::Some(#takes_value),))
2494 })
2495 .collect();
2496 let own_longs: Vec<String> = fields
2499 .iter()
2500 .filter(|f| !f.two_dashes)
2501 .flat_map(|f| spellings(f).into_iter().map(str::to_owned))
2502 .collect();
2503 let prefixes: Vec<u8> = fields
2504 .iter()
2505 .filter(|f| f.prefix)
2506 .filter_map(|f| f.short().map(|c| c as u8))
2507 .collect();
2508 let flatten_prefixes = flattens.iter().map(|(_, ty)| {
2509 quote!(&& !__name.first().is_some_and(|__c| <#ty as __wa::Flatten>::is_prefix(*__c)))
2510 });
2511 let lexer = if plus_options {
2512 quote!(__wa::arg_plus)
2513 } else {
2514 quote!(__wa::arg)
2515 };
2516 if !plus_options {
2517 if let Some(f) = fields.iter().find(|f| f.plus.is_some()) {
2518 return Err(syn::Error::new(
2519 f.ident.span(),
2520 "`plus` needs `#[arg(plus_options)]` on the struct",
2521 ));
2522 }
2523 }
2524 let plus_match = plus_options.then(|| {
2525 quote! {
2526 __wa::Arg::Short(__flag) if __flag.plus => match __flag.letter {
2527 #(#plus_arms)*
2528 _ => #flatten_arg if !__globals.bind(&__arg, __input)? { #unexpected; }
2529 },
2530 }
2531 });
2532 let lex = if opted.is_empty() {
2533 quote!(#lexer(__input)?)
2534 } else {
2535 let digits: Vec<LitByteStr> = fields
2536 .iter()
2537 .filter_map(|f| f.short().filter(char::is_ascii_digit))
2538 .map(|c| LitByteStr::new(format!("-{c}").as_bytes(), Span::call_site()))
2539 .collect();
2540 let not_a_short =
2541 (!digits.is_empty()).then(|| quote!(&&!matches!(__input.front(), #(#digits)|*)));
2542 quote! {
2543 match matches!(__position, #(#opted)|*) #not_a_short {
2544 true => match __wa::number(__input) {
2545 ::core::result::Result::Ok(__word) => __wa::Arg::Word(__word),
2546 ::core::result::Result::Err(_) => #lexer(__input)?,
2547 },
2548 false => #lexer(__input)?,
2549 }
2550 }
2551 };
2552 let start = default_subcommand
2553 .is_some()
2554 .then(|| quote!(let __start = *__input;));
2555 let help_on_empty = arg_required_else_help.then(|| {
2556 quote! {
2557 if __input.is_empty() {
2558 return ::core::result::Result::Err(
2559 __wa::Error::bare_help(<Self as ::winnow_args::Args>::HELP),
2560 );
2561 }
2562 }
2563 });
2564 if default_subcommand.is_some() && subcommand.is_none() {
2565 return Err(syn::Error::new(
2566 input.ident.span(),
2567 "`default_subcommand` needs an `#[arg(subcommand)]` field",
2568 ));
2569 }
2570 let declares_long = |name: &str| fields.iter().any(|f| f.longs().contains(&name));
2573 let declares_short = |c: char| fields.iter().any(|f| f.shorts().contains(&c));
2574 let help = quote!(<Self as ::winnow_args::Args>::HELP);
2575 let help_long = (!disable_help_flag && !declares_long("help")).then(
2576 || quote!(b"help" => return ::core::result::Result::Err(__wa::Error::help(#help, true)),),
2577 );
2578 let help_short = (!disable_help_flag && !disable_help_short && !declares_short('h')).then(
2579 || quote!('h' => return ::core::result::Result::Err(__wa::Error::help(#help, false)),),
2580 );
2581 let with_version = version.is_some() && !disable_version_flag;
2582 let version_long = (with_version && !declares_long("version")).then(
2583 || quote!(b"version" => return ::core::result::Result::Err(__wa::Error::version(#help)),),
2584 );
2585 let version_short = (with_version && !declares_short('V'))
2586 .then(|| quote!('V' => return ::core::result::Result::Err(__wa::Error::version(#help)),));
2587 let help_flag = help_long.is_some();
2589 let help_short_flag = help_short.is_some();
2590 let version_short_flag = version_short.is_some();
2591 let late = !flattens.is_empty() || sequence.is_some();
2595 let late_if = |on: bool, test: TokenStream2, error: TokenStream2| {
2596 (late && on).then(|| quote!(if #test { return ::core::result::Result::Err(#error); } else))
2597 };
2598 let late_long: TokenStream2 = [
2599 late_if(
2600 help_long.is_some(),
2601 quote!(__flag.name == b"help"),
2602 quote!(__wa::Error::help(#help, true)),
2603 ),
2604 late_if(
2605 version_long.is_some(),
2606 quote!(__flag.name == b"version"),
2607 quote!(__wa::Error::version(#help)),
2608 ),
2609 ]
2610 .into_iter()
2611 .flatten()
2612 .collect();
2613 let late_short: TokenStream2 = [
2614 late_if(
2615 help_short.is_some(),
2616 quote!(__flag.letter == 'h'),
2617 quote!(__wa::Error::help(#help, false)),
2618 ),
2619 late_if(
2620 version_short.is_some(),
2621 quote!(__flag.letter == 'V'),
2622 quote!(__wa::Error::version(#help)),
2623 ),
2624 ]
2625 .into_iter()
2626 .flatten()
2627 .collect();
2628 let keep = |arm: Option<TokenStream2>| arm.filter(|_| !late);
2629 let (help_long, help_short) = (keep(help_long), keep(help_short));
2630 let (version_long, version_short) = (keep(version_long), keep(version_short));
2631
2632 let (lenient_bundle, save_token, unknown_flag) = if unknown_flags_value
2642 || unknown.is_some()
2643 || sequence_unknown
2644 {
2645 let next = (!positionals.is_empty() || trailing.is_some()).then(|| quote!(continue;));
2647 let (sink, sink_next) = match (&unknown, sequence_ty.filter(|_| sequence_unknown)) {
2648 (_, Some(ty)) => (
2649 quote! {
2650 match <#ty as ::winnow_args::Occurrence>::from_unknown(&__word)? {
2651 ::core::option::Option::Some(__item) => __sequence.push(__item),
2652 ::core::option::Option::None => {
2653 return ::core::result::Result::Err(__wa::Error::unknown_flag(
2654 __word.offset,
2655 ::std::string::String::from_utf8_lossy(&__word.value),
2656 ));
2657 }
2658 }
2659 },
2660 quote!(continue;),
2661 ),
2662 (Some((_, ty)), None) => (
2663 quote!(__unknown.push(__word.convert::<#ty>("<UNKNOWN>")?);),
2664 quote!(continue;),
2665 ),
2666 (None, None) => (
2667 quote! {
2668 let __arg = __wa::Arg::Word(__word);
2669 #positional_match
2670 },
2671 quote!(#next),
2672 ),
2673 };
2674 let theirs = match sequence_ty {
2675 Some(ty) => quote! {
2676 __c => match <#ty as ::winnow_args::Occurrence>::short(__c) {
2677 ::core::option::Option::None => __globals.short(__c),
2678 __known => __known,
2679 },
2680 },
2681 None => quote!(_ => __globals.short(__c),),
2682 };
2683 let builtin = help_short_flag
2685 .then(|| quote!('h' => ::core::option::Option::Some(false),))
2686 .into_iter()
2687 .chain(version_short_flag.then(|| quote!('V' => ::core::option::Option::Some(false),)));
2688 (
2689 quote! {
2690 if let ::core::option::Option::Some(__word) = __wa::unknown_bundle(__input, |__c| match __c {
2691 #(#own_shorts)*
2692 #(#builtin)*
2693 #(#flatten_shorts)*
2694 #theirs
2695 }) {
2696 #sink
2697 #sink_next
2698 }
2699 },
2700 quote!(let __token = __input.front();),
2701 quote! {
2702 let __word = __wa::Word {
2703 value: __wa::BStr::new(__token),
2704 offset: __flag.offset,
2705 after_separator: false,
2706 };
2707 #sink
2708 },
2709 )
2710 } else {
2711 (quote!(), quote!(), quote!(#unexpected;))
2712 };
2713 let mut names = Vec::new();
2714 if long_only {
2715 names.clone_from(&own_longs);
2716 names.extend(help_long.is_some().then(|| "help".to_owned()));
2717 names.extend(version_long.is_some().then(|| "version".to_owned()));
2718 }
2719 let long_only_lex =
2720 long_only && (!names.is_empty() || sequence_ty.is_some() || !flattens.is_empty());
2721 let lex = if long_only_lex {
2722 let own = matches_name(&names);
2723 let (their_names, their_prefixes) = match sequence_ty {
2724 Some(ty) => (
2725 quote!(|| <#ty as ::winnow_args::Occurrence>::is_long(__name)),
2726 quote! {
2727 && !__name.first().is_some_and(|__c| {
2728 <#ty as ::winnow_args::Occurrence>::PREFIXES.contains(__c)
2729 })
2730 },
2731 ),
2732 None => (quote!(), quote!()),
2733 };
2734 let not_prefixed = (!prefixes.is_empty()).then(
2735 || quote!(!matches!(__name.first(), ::core::option::Option::Some(#(#prefixes)|*)) &&),
2736 );
2737 quote! {
2738 match __wa::long_only(__input, |__name| {
2739 #not_prefixed (#own #their_names #(#flatten_longs)*) #their_prefixes #(#flatten_prefixes)*
2740 }) {
2741 ::core::option::Option::Some(__arg) => __arg,
2742 ::core::option::Option::None => {
2743 #lenient_bundle
2744 #lex
2745 }
2746 }
2747 }
2748 } else {
2749 lex
2750 };
2751 let lenient_bundle = if long_only_lex {
2752 quote!()
2753 } else {
2754 lenient_bundle
2755 };
2756
2757 let help_version = match &version {
2758 Some(v) if with_version => quote!(::core::option::Option::Some(#v)),
2759 _ => quote!(::core::option::Option::None),
2760 };
2761 let help_items = fields
2762 .iter()
2763 .filter(|f| !matches!(f.role, Role::Subcommand))
2764 .map(|f| f.help_item());
2765 let (help_subcommands, subcommand_required) = match subcommand {
2766 Some(f) => {
2767 let ty = subcommand_type(f);
2768 (
2769 quote!(<#ty as ::winnow_args::Args>::HELP.subcommands),
2770 matches!(f.kind, Kind::Required(_)),
2771 )
2772 }
2773 None => (quote!(&[]), false),
2774 };
2775 let displaced = rules.displaced_slots(&fields);
2776 let env_fallbacks: Vec<TokenStream2> = fields
2777 .iter()
2778 .map(|f| f.fallback(true, rules.is_displaced(&fields, f)))
2779 .collect();
2780 let default_fallbacks: Vec<TokenStream2> = fields
2781 .iter()
2782 .map(|f| f.fallback(false, rules.is_displaced(&fields, f)))
2783 .collect();
2784 let exclusive = rules.exclusive(&fields, &groups);
2785 let supplied = rules.supplied(&fields);
2786 let default_ifs = rules.default_ifs(&fields);
2787 let required = rules.required(&fields, &groups);
2788 let build = fields.iter().map(|f| {
2789 let ident = &f.ident;
2790 let slot = slot(ident);
2791 match &f.kind {
2792 Kind::Required(ty) if f.keywords => {
2793 let display = LitStr::new(&f.display(), Span::call_site());
2794 quote!(#ident: __wa::keywords::<#ty>(&#slot, #display, __input.offset())?)
2795 }
2796 Kind::Switch if f.negate.is_some() => quote!(#ident: #slot.unwrap_or(false)),
2797 Kind::Switch | Kind::Count(_) | Kind::Optional(_) | Kind::Many(_) => {
2798 quote!(#ident: #slot)
2799 }
2800 Kind::Required(_) => {
2801 let display = LitStr::new(&f.display(), Span::call_site());
2802 let error = match f.role {
2803 Role::Positional { .. } => {
2804 quote!(__wa::Error::missing_argument(__input.offset(), #display))
2805 }
2806 Role::Flag { .. } => {
2807 quote!(__wa::Error::missing_required(__input.offset(), #display))
2808 }
2809 Role::Subcommand => quote!(__wa::Error::missing_subcommand(__input.offset())),
2810 };
2811 quote! {
2812 #ident: match #slot {
2813 ::core::option::Option::Some(v) => v,
2814 ::core::option::Option::None => {
2815 return ::core::result::Result::Err(#error);
2816 }
2817 }
2818 }
2819 }
2820 }
2821 });
2822 let build: Vec<TokenStream2> = build.collect();
2823 let field_reads: Vec<TokenStream2> = fields
2827 .iter()
2828 .map(|f| &f.ident)
2829 .chain(skipped.iter().copied())
2830 .chain(sequence.iter().chain(&unknown).map(|(ident, _)| ident))
2831 .chain(flattens.iter().map(|(ident, _)| ident))
2832 .map(|ident| quote!(let _ = &__built.#ident;))
2833 .collect();
2834 let flatten_build: Vec<TokenStream2> = flatten_build.collect();
2835 let flattenable = positionals.is_empty()
2838 && trailing.is_none()
2839 && subcommand.is_none()
2840 && sequence.is_none()
2841 && unknown.is_none()
2842 && !unknown_flags_value
2843 && input.generics.params.is_empty()
2844 && !fields.iter().any(|f| f.keywords || f.is_global());
2845 let flatten_impl = flattenable.then(|| {
2846 let vis = &input.vis;
2847 let slots_name = format_ident!("__{}WinnowArgsSlots", name);
2848 let displaced_idents: Vec<Ident> = fields
2849 .iter()
2850 .filter(|f| rules.is_displaced(&fields, f))
2851 .map(|f| displaced_flag(&f.ident))
2852 .collect();
2853 let nested: Vec<(Ident, &Type)> = flattens
2854 .iter()
2855 .map(|(ident, ty)| (flat_slot(ident), ty))
2856 .collect();
2857 let decls = slot_parts.iter().map(|(ident, ty, _)| quote!(#ident: #ty,));
2858 let inits = slot_parts
2859 .iter()
2860 .map(|(ident, _, init)| quote!(#ident: #init,));
2861 let all: Vec<&Ident> = slot_parts
2862 .iter()
2863 .map(|(ident, _, _)| ident)
2864 .chain(&displaced_idents)
2865 .chain(nested.iter().map(|(n, _)| n))
2866 .collect();
2867 let names: Vec<TokenStream2> = all.iter().map(|ident| quote!(#ident,)).collect();
2868 let mutable: Vec<TokenStream2> = all.iter().map(|ident| quote!(mut #ident,)).collect();
2869 let displaced_decls = displaced_idents.iter().map(|d| quote!(#d: bool,));
2870 let displaced_inits = displaced_idents.iter().map(|d| quote!(#d: false,));
2871 let nested_decls = nested
2872 .iter()
2873 .map(|(n, ty)| quote!(#n: <#ty as ::winnow_args::__private::Flatten>::Slots,));
2874 let nested_inits = nested
2875 .iter()
2876 .map(|(n, _)| quote!(#n: ::core::default::Default::default(),));
2877 let (bind_long, bind_short, bind_plus) = arms(&|_| true, quote!(true));
2878 let bind_nested: TokenStream2 = nested
2879 .iter()
2880 .map(|(n, ty)| {
2881 quote!(if <#ty as __wa::Flatten>::bind(&mut #n, &__arg, __input)? { true } else)
2882 })
2883 .collect();
2884 let plus = if plus_options {
2886 quote! {
2887 __wa::Arg::Short(__flag) if __flag.plus => match __flag.letter {
2888 #(#bind_plus)*
2889 _ => #bind_nested { false }
2890 },
2891 }
2892 } else {
2893 quote!(__wa::Arg::Short(__flag) if __flag.plus => #bind_nested { false },)
2894 };
2895 let own_longs = matches_name(&own_longs);
2896 let own_prefixes = if prefixes.is_empty() {
2897 quote!(false)
2898 } else {
2899 quote!(matches!(__c, #(#prefixes)|*))
2900 };
2901 let nested_prefixes = nested
2902 .iter()
2903 .map(|(_, ty)| quote!(|| <#ty as __wa::Flatten>::is_prefix(__c)));
2904 quote! {
2905 #[doc(hidden)]
2906 #[allow(clippy::all, clippy::pedantic, clippy::nursery, clippy::restriction)]
2907 #vis struct #slots_name {
2908 #(#decls)*
2909 #(#displaced_decls)*
2910 #(#nested_decls)*
2911 }
2912
2913 #[automatically_derived]
2914 impl ::core::default::Default for #slots_name {
2915 fn default() -> Self {
2916 Self {
2917 #(#inits)*
2918 #(#displaced_inits)*
2919 #(#nested_inits)*
2920 }
2921 }
2922 }
2923
2924 #[automatically_derived]
2925 #[allow(
2926 unused_mut,
2927 unused_variables,
2928 unreachable_code,
2929 clippy::all,
2930 clippy::pedantic,
2931 clippy::nursery,
2932 clippy::restriction
2933 )]
2934 impl ::winnow_args::__private::Flatten for #name {
2935 type Slots = #slots_name;
2936
2937 fn bind<'__i>(
2938 __slots: &mut #slots_name,
2939 __arg: &::winnow_args::Arg<'__i>,
2940 __input: &mut ::winnow_args::Argv<'__i>,
2941 ) -> ::core::result::Result<bool, ::winnow_args::Error> {
2942 use ::winnow_args::__private as __wa;
2943 let __arg = *__arg;
2944 let #slots_name { #(#mutable)* } = ::core::mem::take(__slots);
2945 let __bound = match __arg {
2946 __wa::Arg::Long(__flag) => match __flag.name {
2947 #(#bind_long)*
2948 _ => #bind_nested { false }
2949 },
2950 #plus
2951 __wa::Arg::Short(__flag) => match __flag.letter {
2952 #(#bind_short)*
2953 _ => #bind_nested { false }
2954 },
2955 _ => false,
2956 };
2957 *__slots = #slots_name { #(#names)* };
2958 ::core::result::Result::Ok(__bound)
2959 }
2960
2961 fn short(__c: char) -> ::core::option::Option<bool> {
2962 use ::winnow_args::__private as __wa;
2963 match __c {
2964 #(#own_shorts)*
2965 #(#flatten_shorts)*
2966 _ => ::core::option::Option::None,
2967 }
2968 }
2969
2970 fn is_prefix(__c: u8) -> bool {
2971 use ::winnow_args::__private as __wa;
2972 #own_prefixes #(#nested_prefixes)*
2973 }
2974
2975 fn is_long(__name: &[u8]) -> bool {
2976 use ::winnow_args::__private as __wa;
2977 #own_longs #(#flatten_longs)*
2978 }
2979
2980 fn finish(
2981 __slots: #slots_name,
2982 __input: &::winnow_args::Argv<'_>,
2983 ) -> ::core::result::Result<Self, ::winnow_args::Error> {
2984 use ::winnow_args::__private as __wa;
2985 let #slots_name { #(#mutable)* } = __slots;
2986 #(#env_fallbacks)*
2987 #exclusive
2988 #supplied
2989 #default_ifs
2990 #(#default_fallbacks)*
2991 #required
2992 let __built = Self {
2993 #(#build,)*
2994 #(#skipped: ::core::default::Default::default(),)*
2995 #(#flatten_build)*
2996 };
2997 #(#field_reads)*
2998 ::core::result::Result::Ok(__built)
2999 }
3000 }
3001 }
3002 });
3003 let own_items = quote!(&[#(#help_items),*]);
3004 let positional_clash = sequence_ty
3006 .filter(|_| !positionals.is_empty() || trailing.is_some())
3007 .map(|ty| {
3008 quote! {
3009 const _: () = assert!(
3010 !<#ty as ::winnow_args::Occurrence>::POSITIONAL,
3011 "the sequence enum has a positional variant, which takes every word: this struct's positionals would never be filled",
3012 );
3013 }
3014 });
3015 let more: Vec<TokenStream2> = flattens
3017 .iter()
3018 .map(|(_, ty)| quote!(<#ty as ::winnow_args::Args>::HELP.items))
3019 .chain(
3020 sequence_ty
3021 .iter()
3022 .map(|ty| quote!(<#ty as ::winnow_args::Occurrence>::ITEMS)),
3023 )
3024 .collect();
3025 let items = if more.is_empty() {
3026 own_items
3027 } else {
3028 quote!({
3029 const __OWN: &[::winnow_args::help::Item] = #own_items;
3030 const _: () = assert!(
3031 !::winnow_args::help::items_clash(&[__OWN, #(#more),*]),
3032 "a flattened struct or the sequence enum declares a flag that this struct, or another of them, also declares",
3033 );
3034 #positional_clash
3035 const __N: usize = __OWN.len() #(+ #more.len())*;
3036 const __ALL: [::winnow_args::help::Item; __N] = ::winnow_args::__private::concat_items::<__N>(
3037 &[__OWN, #(#more),*],
3038 );
3039 &__ALL
3040 })
3041 };
3042
3043 Ok(quote! {
3044 impl #impl_generics ::winnow_args::Args for #name #ty_generics #where_clause {
3045 const HELP: &'static ::winnow_args::help::Command = &::winnow_args::help::Command {
3046 name: #program_name,
3047 about: #about,
3048 long_about: #long_about,
3049 after_help: #after_help,
3050 after_long_help: #after_long_help,
3051 items: #items,
3052 subcommands: #help_subcommands,
3053 subcommand_required: #subcommand_required,
3054 help_flag: #help_flag,
3055 help_short: #help_short_flag,
3056 version: #help_version,
3057 };
3058
3059 fn parse_argv_with(
3060 __input: &mut ::winnow_args::Argv<'_>,
3061 __globals: &mut dyn ::winnow_args::Globals,
3062 ) -> ::core::result::Result<Self, ::winnow_args::Error> {
3063 use ::winnow_args::__private as __wa;
3064 #(#slots)*
3065 #sequence_slot
3066 #unknown_slot
3067 #(#flatten_slots)*
3068 #(#displaced)*
3069 #position
3070 #filled_slot
3071 #help_on_empty
3072 while !__input.is_empty() {
3073 #start
3074 #lenient_bundle
3075 #save_token
3076 #bundle_start
3077 let __arg = #lex;
3078 match __arg {
3079 __wa::Arg::Long(__flag) => match __flag.name {
3080 #(#long_arms)*
3081 #help_long
3082 #version_long
3083 _ => #sequence_arg #flatten_arg #late_long if !__globals.bind(&__arg, __input)? { #unknown_flag },
3084 },
3085 #plus_match
3086 __wa::Arg::Short(__flag) => match __flag.letter {
3087 #(#short_arms)*
3088 #help_short
3089 #version_short
3090 _ => #sequence_arg #flatten_arg #late_short if !__globals.bind(&__arg, __input)? { #unknown_flag },
3091 },
3092 #separator_arm
3093 #word_arm
3094 }
3095 #bundle_end
3096 }
3097 #(#env_fallbacks)*
3098 #exclusive
3099 #supplied
3100 #default_ifs
3101 #(#default_fallbacks)*
3102 #required
3103 let __built = Self {
3104 #(#build,)*
3105 #(#skipped: ::core::default::Default::default(),)*
3106 #sequence_build
3107 #unknown_build
3108 #(#flatten_build)*
3109 };
3110 #(#field_reads)*
3111 ::core::result::Result::Ok(__built)
3112 }
3113 }
3114
3115 #flatten_impl
3116 })
3117}
3118
3119struct Group {
3121 name: String,
3122 required: bool,
3123 multiple: bool,
3124}
3125
3126#[derive(Default)]
3128struct StructOptions {
3129 groups: Vec<Group>,
3130 restart_token: Option<String>,
3132 default_subcommand: Option<String>,
3134 arg_required_else_help: bool,
3136 name: String,
3138 version: Option<TokenStream2>,
3140 about: Option<String>,
3142 long_about: Option<String>,
3143 after_help: String,
3144 after_long_help: String,
3145 disable_help_flag: bool,
3146 disable_help_short: bool,
3148 disable_version_flag: bool,
3149 disable_help_subcommand: bool,
3150 unknown_flags_value: bool,
3152 long_only: bool,
3154 plus_options: bool,
3156}
3157
3158fn struct_options(input: &DeriveInput) -> syn::Result<StructOptions> {
3159 let mut options = StructOptions::default();
3160 for attr in input.attrs.iter().filter(|a| is_ours(a)) {
3161 attr.parse_nested_meta(|meta| {
3162 if meta.path.is_ident("restart_token") {
3163 options.restart_token = Some(meta.value()?.parse::<LitStr>()?.value());
3164 return Ok(());
3165 }
3166 if meta.path.is_ident("default_subcommand") {
3167 options.default_subcommand = Some(meta.value()?.parse::<LitStr>()?.value());
3168 return Ok(());
3169 }
3170 if meta.path.is_ident("arg_required_else_help") {
3171 options.arg_required_else_help = true;
3172 return Ok(());
3173 }
3174 if meta.path.is_ident("long_only") {
3175 options.long_only = true;
3176 return Ok(());
3177 }
3178 if meta.path.is_ident("plus_options") {
3179 options.plus_options = true;
3180 return Ok(());
3181 }
3182 if meta.path.is_ident("unknown_flags") {
3183 let mode = meta.value()?.parse::<LitStr>()?;
3184 options.unknown_flags_value = match mode.value().as_str() {
3185 "value" => true,
3186 "error" => false,
3187 _ => {
3188 return Err(syn::Error::new(
3189 mode.span(),
3190 "expected \"value\" or \"error\"",
3191 ));
3192 }
3193 };
3194 return Ok(());
3195 }
3196 let text = |meta: &syn::meta::ParseNestedMeta<'_>| -> syn::Result<String> {
3197 Ok(meta.value()?.parse::<LitStr>()?.value())
3198 };
3199 if meta.path.is_ident("name") {
3200 options.name = text(&meta)?;
3201 return Ok(());
3202 }
3203 if meta.path.is_ident("version") {
3204 options.version = Some(if meta.input.peek(syn::Token![=]) {
3205 let version = meta.value()?.parse::<LitStr>()?;
3206 quote!(#version)
3207 } else {
3208 quote!(::core::env!("CARGO_PKG_VERSION"))
3209 });
3210 return Ok(());
3211 }
3212 for (key, slot) in [
3213 ("about", &mut options.about),
3214 ("long_about", &mut options.long_about),
3215 ] {
3216 if meta.path.is_ident(key) {
3217 *slot = Some(text(&meta)?);
3218 return Ok(());
3219 }
3220 }
3221 for (key, slot) in [
3222 ("after_help", &mut options.after_help),
3223 ("after_long_help", &mut options.after_long_help),
3224 ] {
3225 if meta.path.is_ident(key) {
3226 *slot = text(&meta)?;
3227 return Ok(());
3228 }
3229 }
3230 for (key, slot) in [
3231 ("disable_help_flag", &mut options.disable_help_flag),
3232 ("disable_help_short", &mut options.disable_help_short),
3233 ("disable_version_flag", &mut options.disable_version_flag),
3234 (
3235 "disable_help_subcommand",
3236 &mut options.disable_help_subcommand,
3237 ),
3238 ] {
3239 if meta.path.is_ident(key) {
3240 *slot = true;
3241 return Ok(());
3242 }
3243 }
3244 if !meta.path.is_ident("group") {
3245 return Err(meta.error(
3246 "expected `group(\"name\", ...)`, `restart_token`, `default_subcommand`, \
3247 `arg_required_else_help`, `unknown_flags`, `long_only`, `plus_options`, `name`, `version`, `about`, \
3248 `long_about`, `after_help`, `after_long_help` or a `disable_*` option",
3249 ));
3250 }
3251 let content;
3252 syn::parenthesized!(content in meta.input);
3253 let name = content.parse::<LitStr>()?.value();
3254 let (mut required, mut multiple) = (false, false);
3255 while !content.is_empty() {
3256 content.parse::<syn::Token![,]>()?;
3257 let flag = content.parse::<Ident>()?;
3258 match flag.to_string().as_str() {
3259 "required" => required = true,
3260 "multiple" => multiple = true,
3261 _ => {
3262 return Err(syn::Error::new(
3263 flag.span(),
3264 "expected `required` or `multiple`",
3265 ));
3266 }
3267 }
3268 }
3269 options.groups.push(Group {
3270 name,
3271 required,
3272 multiple,
3273 });
3274 Ok(())
3275 })?;
3276 }
3277 Ok(options)
3278}
3279
3280struct Rules {
3282 conflicts: Vec<(usize, usize)>,
3284 beats: Vec<Vec<usize>>,
3286 requires: Vec<(usize, usize)>,
3288 required_unless: Vec<(usize, Vec<usize>)>,
3290 members: Vec<Vec<usize>>,
3292 default_ifs: Vec<(usize, usize, Option<String>, String)>,
3294}
3295
3296impl Rules {
3297 fn new(fields: &[Field], groups: &[Group]) -> syn::Result<Self> {
3298 let resolve = |from: &Field, selector: &str| -> syn::Result<usize> {
3299 fields
3300 .iter()
3301 .position(
3302 |f| match (selector.strip_prefix("--"), selector.strip_prefix('-')) {
3303 (Some(long), _) => f.longs().contains(&long),
3304 (None, Some(short)) => {
3305 let mut chars = short.chars();
3306 chars
3307 .next()
3308 .is_some_and(|c| chars.next().is_none() && f.shorts().contains(&c))
3309 }
3310 (None, None) => {
3311 matches!(&f.role, Role::Positional { name, .. } if name == selector)
3312 || f.ident == selector
3313 }
3314 },
3315 )
3316 .ok_or_else(|| {
3317 let negated = selector
3319 .strip_prefix("--")
3320 .and_then(|long| fields.iter().find(|f| f.negate.as_deref() == Some(long)));
3321 let message = match negated {
3322 Some(f) => format!(
3323 "`{selector}` is the negation of `{}`: name that flag instead",
3324 f.display()
3325 ),
3326 None => format!("`{selector}` names no flag or positional of this struct"),
3327 };
3328 syn::Error::new(from.ident.span(), message)
3329 })
3330 };
3331 let mut rules = Rules {
3332 conflicts: Vec::new(),
3333 beats: vec![Vec::new(); fields.len()],
3334 requires: Vec::new(),
3335 required_unless: Vec::new(),
3336 members: vec![Vec::new(); groups.len()],
3337 default_ifs: Vec::new(),
3338 };
3339 for (i, f) in fields.iter().enumerate() {
3340 for selector in &f.conflicts {
3341 let j = resolve(f, selector)?;
3342 let pair = (i.min(j), i.max(j));
3343 if !rules.conflicts.contains(&pair) {
3344 rules.conflicts.push(pair);
3345 }
3346 }
3347 for selector in &f.overrides {
3348 let j = resolve(f, selector)?;
3349 if !rules.beats[i].contains(&j) {
3350 rules.beats[i].push(j);
3351 }
3352 if !rules.beats[j].contains(&i) {
3353 rules.beats[j].push(i);
3354 }
3355 }
3356 for selector in &f.requires {
3357 rules.requires.push((i, resolve(f, selector)?));
3358 }
3359 for (selector, equals, value) in &f.default_ifs {
3360 let other = resolve(f, selector)?;
3361 let comparable = match &fields[other].kind {
3362 Kind::Count(_) => false,
3363 Kind::Switch => equals.as_deref().is_none_or(is_bool_word),
3364 _ => !fields[other].keywords,
3365 };
3366 if equals.is_some() && !comparable {
3367 return Err(syn::Error::new(
3368 f.ident.span(),
3369 format!(
3370 "`{selector}` has no value to compare: use `default_if(\"{selector}\", \"…\")`"
3371 ),
3372 ));
3373 }
3374 rules
3375 .default_ifs
3376 .push((i, other, equals.clone(), value.clone()));
3377 }
3378 if !f.required_unless.is_empty() {
3379 let others = f
3380 .required_unless
3381 .iter()
3382 .map(|selector| resolve(f, selector))
3383 .collect::<syn::Result<_>>()?;
3384 rules.required_unless.push((i, others));
3385 }
3386 if let Some(group) = &f.group {
3387 let g = groups
3388 .iter()
3389 .position(|g| &g.name == group)
3390 .ok_or_else(|| {
3391 syn::Error::new(
3392 f.ident.span(),
3393 format!("no `#[arg(group(\"{group}\"))]` on the struct"),
3394 )
3395 })?;
3396 rules.members[g].push(i);
3397 }
3398 }
3399 if let Some((g, _)) = groups
3400 .iter()
3401 .zip(&rules.members)
3402 .find(|(g, members)| g.required && members.is_empty())
3403 {
3404 return Err(syn::Error::new(
3405 Span::call_site(),
3406 format!("group `{}` is required, but no field is in it", g.name),
3407 ));
3408 }
3409 Ok(rules)
3410 }
3411
3412 fn is_displaced(&self, fields: &[Field], f: &Field) -> bool {
3414 !self.beats[index_of(fields, f)].is_empty()
3415 }
3416
3417 fn displaced_slots(&self, fields: &[Field]) -> Vec<TokenStream2> {
3419 fields
3420 .iter()
3421 .enumerate()
3422 .filter(|(i, _)| !self.beats[*i].is_empty())
3423 .map(|(_, f)| {
3424 let displaced = displaced_flag(&f.ident);
3425 quote!(let mut #displaced = false;)
3426 })
3427 .collect()
3428 }
3429
3430 fn displace(&self, fields: &[Field], f: &Field) -> TokenStream2 {
3432 let i = index_of(fields, f);
3433 if self.beats[i].is_empty() {
3434 return quote!();
3435 }
3436 let standing = displaced_flag(&f.ident);
3437 let clears = self.beats[i].iter().map(|&j| {
3438 let other = &fields[j];
3439 let slot = slot(&other.ident);
3440 let displaced = displaced_flag(&other.ident);
3441 let clear = match &other.kind {
3442 _ if other.keywords => quote!(#slot.clear();),
3443 Kind::Switch if other.negate.is_some() => {
3444 quote!(#slot = ::core::option::Option::None;)
3445 }
3446 Kind::Switch => quote!(#slot = false;),
3447 Kind::Count(_) => quote!(#slot = 0;),
3448 Kind::Optional(_) | Kind::Required(_) => {
3449 quote!(#slot = ::core::option::Option::None;)
3450 }
3451 Kind::Many(_) => quote!(#slot.clear();),
3452 };
3453 quote!(#clear #displaced = true;)
3454 });
3455 quote!(#standing = false; #(#clears)*)
3456 }
3457
3458 fn exclusive(&self, fields: &[Field], groups: &[Group]) -> TokenStream2 {
3460 let pairs = self.conflicts.iter().map(|&(a, b)| {
3461 let (fa, fb) = (&fields[a], &fields[b]);
3462 let (has_a, has_b) = (fa.has(), fb.has());
3463 let (name_a, name_b) = (fa.display(), fb.display());
3464 quote! {
3465 if #has_a && #has_b {
3466 return ::core::result::Result::Err(
3467 __wa::Error::conflict(__input.offset(), #name_a, #name_b),
3468 );
3469 }
3470 }
3471 });
3472 let at_most_one = groups.iter().zip(&self.members).filter(|(g, _)| !g.multiple).map(|(_, members)| {
3473 let checks = members.iter().map(|&m| {
3474 let has = fields[m].has();
3475 let name = fields[m].display();
3476 quote! {
3477 if #has {
3478 if let ::core::option::Option::Some(__first) = __first {
3479 return ::core::result::Result::Err(
3480 __wa::Error::conflict(__input.offset(), __first, #name),
3481 );
3482 }
3483 __first = ::core::option::Option::Some(#name);
3484 }
3485 }
3486 });
3487 quote! {{
3488 let mut __first: ::core::option::Option<&'static str> = ::core::option::Option::None;
3489 #(#checks)*
3490 }}
3491 });
3492 quote!(#(#pairs)* #(#at_most_one)*)
3493 }
3494
3495 fn default_ifs(&self, fields: &[Field]) -> TokenStream2 {
3498 let (mut conditions, mut fills) = (Vec::new(), Vec::new());
3499 for (n, (field, other, equals, value)) in self.default_ifs.iter().enumerate() {
3500 let (f, other) = (&fields[*field], &fields[*other]);
3501 let holds = format_ident!("__default_if_{n}");
3502 let other_slot = slot(&other.ident);
3503 let is = |ty: &Type, equals: &str| {
3504 let literal = LitByteStr::new(equals.as_bytes(), Span::call_site());
3505 quote! {
3506 |__v| <#ty as ::winnow_args::FromArg>::from_arg(__wa::BStr::new(#literal))
3507 .is_ok_and(|__literal| __literal == *__v)
3508 }
3509 };
3510 let condition = match (equals, &other.kind) {
3511 (None, _) => other.has(),
3512 (Some(equals), Kind::Switch) => {
3513 let equals = equals == "true";
3514 match other.negate {
3515 Some(_) => quote!((#other_slot == ::core::option::Option::Some(#equals))),
3516 None => quote!((#other_slot == #equals)),
3517 }
3518 }
3519 (Some(equals), Kind::Optional(ty) | Kind::Required(ty)) => {
3520 let is = is(ty, equals);
3521 quote!(#other_slot.as_ref().is_some_and(#is))
3522 }
3523 (Some(equals), Kind::Many(ty)) => {
3524 let is = is(ty, equals);
3525 quote!(#other_slot.iter().any(#is))
3526 }
3527 (Some(_), Kind::Count(_)) => unreachable!("refused when the rules were read"),
3528 };
3529 conditions.push(quote!(let #holds = #condition;));
3530 let unset = f.unset(self.is_displaced(fields, f));
3531 let slot = slot(&f.ident);
3532 let fill = match &f.kind {
3533 Kind::Switch => {
3534 let value = value == "true";
3535 match f.negate {
3536 Some(_) => quote!(#slot = ::core::option::Option::Some(#value);),
3537 None => quote!(#slot = #value;),
3538 }
3539 }
3540 _ => {
3541 let bytes = LitByteStr::new(value.as_bytes(), Span::call_site());
3542 f.assign(quote!(__wa::BStr::new(#bytes)), &f.display())
3543 }
3544 };
3545 fills.push(quote!(if #holds && #unset { #fill }));
3546 }
3547 quote!(#(#conditions)* #(#fills)*)
3548 }
3549
3550 fn supplied(&self, fields: &[Field]) -> TokenStream2 {
3552 let mut seen = Vec::new();
3553 let snapshots = self.requires.iter().filter_map(|&(i, _)| {
3554 if seen.contains(&i) {
3555 return None;
3556 }
3557 seen.push(i);
3558 let supplied = format_ident!("__supplied_{}", fields[i].ident);
3559 let has = fields[i].has();
3560 Some(quote!(let #supplied = #has;))
3561 });
3562 quote!(#(#snapshots)*)
3563 }
3564
3565 fn required(&self, fields: &[Field], groups: &[Group]) -> TokenStream2 {
3568 let missing = |f: &Field| {
3569 let name = f.display();
3570 match f.role {
3571 Role::Positional { .. } => {
3572 quote!(__wa::Error::missing_argument(__input.offset(), #name))
3573 }
3574 _ => quote!(__wa::Error::missing_required(__input.offset(), #name)),
3575 }
3576 };
3577 let displaced = |f: &Field| {
3579 self.is_displaced(fields, f).then(|| {
3580 let flag = displaced_flag(&f.ident);
3581 quote!(&& !#flag)
3582 })
3583 };
3584 let plain = fields.iter().filter(|f| f.required).map(|f| {
3585 let (has, displaced) = (f.has(), displaced(f));
3586 let error = missing(f);
3587 quote!(if !#has #displaced { return ::core::result::Result::Err(#error); })
3588 });
3589 let unless = self.required_unless.iter().map(|(i, others)| {
3590 let (has, displaced) = (fields[*i].has(), displaced(&fields[*i]));
3591 let others = others.iter().map(|&j| fields[j].has());
3592 let error = missing(&fields[*i]);
3593 quote! {
3594 if !#has #displaced #(&& !#others)* {
3595 return ::core::result::Result::Err(#error);
3596 }
3597 }
3598 });
3599 let one_of = groups
3600 .iter()
3601 .zip(&self.members)
3602 .filter(|(g, _)| g.required)
3603 .map(|(g, members)| {
3604 let name = &g.name;
3605 let has = members.iter().map(|&m| fields[m].has());
3606 let names = members.iter().map(|&m| fields[m].display());
3607 quote! {
3608 if #(!#has)&&* {
3609 return ::core::result::Result::Err(
3610 __wa::Error::missing_one_of(__input.offset(), #name, &[#(#names),*]),
3611 );
3612 }
3613 }
3614 });
3615 let requires = self.requires.iter().map(|&(i, j)| {
3616 let supplied = format_ident!("__supplied_{}", fields[i].ident);
3617 let has = fields[j].has();
3618 let (by, target) = (fields[i].display(), fields[j].display());
3619 quote! {
3620 if #supplied && !#has {
3621 return ::core::result::Result::Err(
3622 __wa::Error::required_by(__input.offset(), #target, #by),
3623 );
3624 }
3625 }
3626 });
3627 quote!(#(#plain)* #(#unless)* #(#one_of)* #(#requires)*)
3628 }
3629}
3630
3631fn vec_field(f: Option<&syn::Field>, shape: &str) -> syn::Result<Option<(Ident, Type)>> {
3633 let Some(f) = f else {
3634 return Ok(None);
3635 };
3636 let ty = last_segment(&f.ty)
3637 .filter(|s| s.ident == "Vec")
3638 .and_then(inner)
3639 .ok_or_else(|| syn::Error::new(f.ty.span(), shape))?;
3640 Ok(Some((f.ident.clone().expect("named field"), ty.clone())))
3641}
3642
3643fn subcommand_type(f: &Field) -> &Type {
3645 match &f.kind {
3646 Kind::Optional(ty) | Kind::Required(ty) => ty,
3647 _ => unreachable!("rejected for subcommands in `field`"),
3648 }
3649}
3650
3651fn displaced_flag(ident: &Ident) -> Ident {
3652 format_ident!("__displaced_{}", ident)
3653}
3654
3655fn index_of(fields: &[Field], f: &Field) -> usize {
3656 fields
3657 .iter()
3658 .position(|g| g.ident == f.ident)
3659 .expect("a field of this struct")
3660}
3661
3662fn slot(ident: &Ident) -> Ident {
3663 format_ident!("__slot_{}", ident)
3664}
3665
3666fn field(f: &syn::Field) -> syn::Result<Field> {
3667 let ident = f.ident.clone().expect("named field");
3668 let bare = ident.to_string().trim_start_matches("r#").to_owned();
3669
3670 let mut short = None;
3671 let mut long = None;
3672 let mut positional = false;
3673 let mut count = false;
3674 let mut subcommand = false;
3675 let mut global = false;
3676 let mut alias = Vec::new();
3677 let mut delimiter = None;
3678 let mut choices = None;
3679 let mut env = None;
3680 let mut default = None;
3681 let mut double_dash = None;
3682 let mut default_missing = None;
3683 let mut negative_numbers = false;
3684 let mut hyphen_values = false;
3685 let mut require_equals = false;
3686 let mut keep_equals = false;
3687 let (mut two_dashes, mut prefix) = (false, false);
3688 let mut values = 1;
3689 let (mut values_max, mut value_terminator) = (None, None);
3690 let (mut default_fn, mut default_note, mut default_ifs) = (None, None, Vec::new());
3691 let mut stop_flags = false;
3692 let mut plus: Option<char> = None;
3693 let mut keywords = false;
3694 let mut short_aliases = Vec::new();
3695 let mut negate: Option<Option<String>> = None;
3696 let (doc_help, doc_long_help) = docs(&f.attrs);
3697 let (mut help, mut long_help, mut heading, mut hide) = (None, None, None, false);
3698 let (mut conflicts, mut overrides, mut requires) = (Vec::new(), Vec::new(), Vec::new());
3699 let (mut required, mut required_unless, mut group) = (false, Vec::new(), None);
3700 let mut value_name = None;
3701 for attr in f.attrs.iter().filter(|a| is_ours(a)) {
3702 attr.parse_nested_meta(|meta| {
3703 if meta.path.is_ident("short") {
3704 let c = if meta.input.peek(syn::Token![=]) {
3705 meta.value()?.parse::<LitChar>()?.value()
3706 } else {
3707 bare.chars()
3708 .next()
3709 .ok_or_else(|| meta.error("cannot infer a short name"))?
3710 };
3711 match short {
3713 None => short = Some(c),
3714 Some(_) => short_aliases.push(c),
3715 }
3716 } else if meta.path.is_ident("long") {
3717 let name = if meta.input.peek(syn::Token![=]) {
3718 meta.value()?.parse::<LitStr>()?.value()
3719 } else {
3720 bare.replace('_', "-")
3721 };
3722 match long {
3724 None => long = Some(name),
3725 Some(_) => alias.push(name),
3726 }
3727 } else if meta.path.is_ident("positional") {
3728 positional = true;
3729 } else if meta.path.is_ident("count") {
3730 count = true;
3731 } else if meta.path.is_ident("subcommand") {
3732 subcommand = true;
3733 } else if meta.path.is_ident("global") {
3734 global = true;
3735 } else if meta.path.is_ident("alias") {
3736 alias.extend(aliases(&meta)?);
3737 } else if meta.path.is_ident("help") {
3738 help = Some(meta.value()?.parse::<LitStr>()?.value());
3739 } else if meta.path.is_ident("long_help") {
3740 long_help = Some(meta.value()?.parse::<LitStr>()?.value());
3741 } else if meta.path.is_ident("help_heading") {
3742 heading = Some(meta.value()?.parse::<LitStr>()?.value());
3743 } else if meta.path.is_ident("hide") {
3744 hide = true;
3745 } else if meta.path.is_ident("negate") {
3746 negate = Some(if meta.input.peek(syn::Token![=]) {
3747 let name = meta.value()?.parse::<LitStr>()?.value();
3748 Some(name.strip_prefix("--").unwrap_or(&name).to_owned())
3750 } else {
3751 None
3752 });
3753 } else if meta.path.is_ident("keywords") {
3754 keywords = true;
3755 } else if meta.path.is_ident("plus") {
3756 plus = Some(meta.value()?.parse::<LitChar>()?.value());
3757 } else if meta.path.is_ident("stop_flags") {
3758 stop_flags = true;
3759 } else if meta.path.is_ident("values") {
3760 let count = meta.value()?.parse::<syn::Expr>()?;
3761 (values, values_max) = value_count(&count)?;
3762 } else if meta.path.is_ident("value_terminator") {
3763 value_terminator = Some(meta.value()?.parse::<LitStr>()?.value());
3764 } else if meta.path.is_ident("default_fn") {
3765 default_fn = Some(meta.value()?.parse::<syn::ExprPath>()?);
3766 } else if meta.path.is_ident("default_note") {
3767 default_note = Some(meta.value()?.parse::<LitStr>()?.value());
3768 } else if meta.path.is_ident("default_if") {
3769 let content;
3770 syn::parenthesized!(content in meta.input);
3771 let list =
3772 syn::punctuated::Punctuated::<LitStr, syn::Token![,]>::parse_terminated(&content)?;
3773 let mut words = list.iter().map(LitStr::value);
3774 default_ifs.push(match (words.next(), words.next(), words.next(), words.next()) {
3775 (Some(selector), Some(value), None, _) => (selector, None, value),
3776 (Some(selector), Some(equals), Some(value), None) => {
3777 (selector, Some(equals), value)
3778 }
3779 _ => {
3780 return Err(meta.error(
3781 "expected `default_if(\"--flag\", \"value\")` or \
3782 `default_if(\"--flag\", \"its value\", \"value\")`",
3783 ));
3784 }
3785 });
3786 } else if meta.path.is_ident("two_dashes") {
3787 two_dashes = true;
3788 } else if meta.path.is_ident("prefix") {
3789 prefix = true;
3790 } else if meta.path.is_ident("require_equals") {
3791 require_equals = true;
3792 } else if meta.path.is_ident("keep_equals") {
3793 keep_equals = true;
3794 } else if meta.path.is_ident("allow_hyphen_values") {
3795 hyphen_values = true;
3796 } else if meta.path.is_ident("allow_negative_numbers") {
3797 negative_numbers = true;
3798 } else if meta.path.is_ident("default_missing") {
3799 default_missing = Some(meta.value()?.parse::<LitStr>()?.value());
3800 } else if meta.path.is_ident("double_dash") {
3801 let mode = meta.value()?.parse::<LitStr>()?;
3802 double_dash = Some(match mode.value().as_str() {
3803 "required" => DoubleDash::Required,
3804 "automatic" => DoubleDash::Automatic,
3805 "optional" => DoubleDash::Optional,
3806 "preserve" => DoubleDash::Preserve,
3807 _ => {
3808 return Err(syn::Error::new(
3809 mode.span(),
3810 "expected \"required\", \"automatic\", \"optional\" or \"preserve\"",
3811 ));
3812 }
3813 });
3814 } else if meta.path.is_ident("conflicts") {
3815 conflicts.extend(aliases(&meta)?);
3816 } else if meta.path.is_ident("overrides") {
3817 overrides.extend(aliases(&meta)?);
3818 } else if meta.path.is_ident("requires") {
3819 requires.extend(aliases(&meta)?);
3820 } else if meta.path.is_ident("required_unless") {
3821 required_unless.extend(aliases(&meta)?);
3822 } else if meta.path.is_ident("required") {
3823 required = true;
3824 } else if meta.path.is_ident("group") {
3825 group = Some(meta.value()?.parse::<LitStr>()?.value());
3826 } else if meta.path.is_ident("env") {
3827 env = Some(meta.value()?.parse::<LitStr>()?.value());
3828 } else if meta.path.is_ident("default") {
3829 default = Some(meta.value()?.parse::<LitStr>()?.value());
3830 } else if meta.path.is_ident("choices") {
3831 let content;
3832 syn::parenthesized!(content in meta.input);
3833 let list =
3834 syn::punctuated::Punctuated::<LitStr, syn::Token![,]>::parse_terminated(&content)?;
3835 choices = Some(list.iter().map(LitStr::value).collect());
3836 } else if meta.path.is_ident("delimiter") {
3837 let c = meta.value()?.parse::<LitChar>()?;
3838 if !c.value().is_ascii() {
3839 return Err(syn::Error::new(c.span(), "a delimiter is one ASCII character"));
3840 }
3841 delimiter = Some(c.value() as u8);
3842 } else if meta.path.is_ident("value_name") {
3843 value_name = Some(meta.value()?.parse::<LitStr>()?.value());
3844 } else {
3845 return Err(meta.error(
3846 "unknown `arg` option; expected one of `short`, `long`, `alias`, `global`, `count`, \
3847 `positional`, `value_name`, `double_dash`, `subcommand`, `flatten`, `sequence`, `unknown`, `skip`, \
3848 `delimiter`, `choices`, `env`, `default`, `default_missing`, `allow_negative_numbers`, \
3849 `allow_hyphen_values`, `require_equals`, `keep_equals`, `negate`, `two_dashes`, `prefix`, `values`, \
3850 `value_terminator`, `default_fn`, `default_note`, `default_if`, \
3851 `stop_flags`, `plus`, `keywords`, `conflicts`, `overrides`, `requires`, `required`, `required_unless`, \
3852 `group`, `help`, `long_help`, `help_heading`, `hide`",
3853 ));
3854 }
3855 Ok(())
3856 })?;
3857 }
3858
3859 let error = |msg: String| Err(syn::Error::new(f.span(), msg));
3860 if delimiter.is_some() && (count || !matches!(kind(&f.ty), Kind::Many(_))) {
3861 return error("`delimiter` needs a `Vec<T>` field to put the pieces in".into());
3862 }
3863 let kind = if count {
3864 Kind::Count(f.ty.clone())
3865 } else {
3866 kind(&f.ty)
3867 };
3868 if default_missing.is_some()
3869 && (positional || subcommand || matches!(kind, Kind::Switch | Kind::Count(_)))
3870 {
3871 return error("`default_missing` is for flags that take a value".into());
3872 }
3873 if (hyphen_values || require_equals)
3874 && (positional || subcommand || matches!(kind, Kind::Switch | Kind::Count(_)))
3875 {
3876 return error(
3877 "`allow_hyphen_values` and `require_equals` are for flags that take a value".into(),
3878 );
3879 }
3880 if negative_numbers && (subcommand || matches!(kind, Kind::Switch | Kind::Count(_))) {
3881 return error("`allow_negative_numbers` is for fields that take a value".into());
3882 }
3883 let tristate =
3884 plus.is_some() && plus == short && matches!(&kind, Kind::Optional(ty) if is_bool(ty));
3885 if plus.is_some()
3886 && (positional || subcommand || !tristate && matches!(kind, Kind::Switch | Kind::Count(_)))
3887 {
3888 return error(
3889 "`plus` is for a flag taking a value, or an `Option<bool>` with the same `short` letter".into(),
3890 );
3891 }
3892 if keywords
3893 && (positional
3894 || subcommand
3895 || !matches!(kind, Kind::Required(_))
3896 || env.is_some()
3897 || default.is_some()
3898 || default_missing.is_some()
3899 || choices.is_some()
3900 || delimiter.is_some())
3901 {
3902 return error(
3903 "`keywords` is for a flag whose type derives `Args`, without `env`, `default`, \
3904 `default_missing`, `choices` or `delimiter`"
3905 .into(),
3906 );
3907 }
3908 if stop_flags && !positional {
3909 return error("`stop_flags` is for positional fields".into());
3910 }
3911 if double_dash.is_some() && !positional {
3912 return error("`double_dash` is for positional fields".into());
3913 }
3914 if (values > 1 || values_max.is_some())
3915 && (positional
3916 || subcommand
3917 || !matches!(kind, Kind::Many(_))
3918 || delimiter.is_some()
3919 || default_missing.is_some())
3920 {
3921 return error(
3922 "`values` is for a `Vec<T>` flag, without `delimiter` or `default_missing`".into(),
3923 );
3924 }
3925 if values_max.is_some() && require_equals {
3926 return error("a range of `values` takes detached words: not with `require_equals`".into());
3927 }
3928 if value_terminator.is_some() && values_max.is_none() {
3929 return error("`value_terminator` ends a range of `values` (`values = 1..`)".into());
3930 }
3931 let valueless = matches!(kind, Kind::Switch | Kind::Count(_)) && negate.is_none();
3932 if default_fn.is_some() && (default.is_some() || subcommand || keywords || valueless) {
3933 return error(
3934 "`default_fn` is for a field that takes a value, or `negate`, without `default`".into(),
3935 );
3936 }
3937 if default_note.is_some() && default_fn.is_none() {
3938 return error("`default_note` describes a `default_fn`".into());
3939 }
3940 if !default_ifs.is_empty() && (subcommand || keywords || matches!(kind, Kind::Count(_))) {
3941 return error("`default_if` is for a switch or a field that takes a value".into());
3942 }
3943 if let (Kind::Switch, Some((.., value))) =
3944 (&kind, default_ifs.iter().find(|d| !is_bool_word(&d.2)))
3945 {
3946 return error(format!(
3947 "a switch's `default_if` value is `true` or `false`, not `{value}`"
3948 ));
3949 }
3950 if two_dashes && (long.is_none() && alias.is_empty() || positional || subcommand) {
3951 return error("`two_dashes` is for flags with a long name".into());
3952 }
3953 if prefix
3954 && (short.is_none_or(|c| !c.is_ascii())
3955 || positional
3956 || subcommand
3957 || matches!(kind, Kind::Switch | Kind::Count(_)))
3958 {
3959 return error("`prefix` is for a value-taking flag with an ASCII `short` letter".into());
3960 }
3961 let flag_value_name =
3962 (!positional && !subcommand && !matches!(kind, Kind::Switch | Kind::Count(_)))
3963 .then(|| value_name.clone().unwrap_or_else(|| bare.to_uppercase()));
3964 let role = if subcommand {
3965 if positional || count || global || short.is_some() || long.is_some() || !alias.is_empty() {
3966 return error("a subcommand field takes no other `arg` options".into());
3967 }
3968 if !matches!(kind, Kind::Optional(_) | Kind::Required(_)) {
3969 return error("a subcommand field is `E` or `Option<E>`".into());
3970 }
3971 Role::Subcommand
3972 } else if positional {
3973 if short.is_some() || long.is_some() || global || !alias.is_empty() {
3974 return error("a positional field has no `short`, `long`, `alias` or `global`".into());
3975 }
3976 if matches!(kind, Kind::Switch | Kind::Count(_)) {
3977 return error("a positional field cannot be `bool` or `count`".into());
3978 }
3979 Role::Positional {
3980 double_dash: double_dash.unwrap_or(DoubleDash::Optional),
3981 name: value_name.unwrap_or_else(|| bare.to_uppercase()),
3982 }
3983 } else {
3984 if short.is_none() && long.is_none() && plus.is_none() {
3987 long = Some(bare.replace('_', "-"));
3988 }
3989 for l in long.iter().chain(&alias) {
3990 if l.is_empty() || l.starts_with('-') || l.contains('=') {
3991 return error(format!("`{l}` is not a usable long name"));
3992 }
3993 }
3994 if let Some(c) = short {
3995 if c == '-' || c == '=' {
3996 return error(format!("`{c}` is not a usable short name"));
3997 }
3998 }
3999 Role::Flag {
4000 short,
4001 long,
4002 aliases: alias,
4003 global,
4004 }
4005 };
4006
4007 if choices.is_some()
4008 && (matches!(kind, Kind::Switch | Kind::Count(_)) || matches!(role, Role::Subcommand))
4009 {
4010 return error("`choices` needs a field that takes a value".into());
4011 }
4012 if (env.is_some() || default.is_some()) && matches!(role, Role::Subcommand) {
4013 return error("a subcommand field takes no `env` or `default`".into());
4014 }
4015 let negate = match negate {
4016 None => None,
4017 Some(_) if !matches!(kind, Kind::Switch) || !matches!(role, Role::Flag { .. }) => {
4018 return error("`negate` is for `bool` flags".into());
4019 }
4020 Some(Some(name)) => Some(name),
4021 Some(None) => match &role {
4022 Role::Flag { long: Some(l), .. } => Some(format!("no-{l}")),
4023 _ => return error("`negate` without a name needs a `long` to prefix".into()),
4024 },
4025 };
4026 if let Some(no) = &negate {
4027 if no.is_empty() || no.starts_with('-') || no.contains('=') {
4028 return error(format!("`{no}` is not a usable long name"));
4029 }
4030 }
4031 match (&default, &negate) {
4032 (Some(d), Some(_)) if d != "true" && d != "false" => {
4033 return error("a negatable flag's `default` is \"true\" or \"false\"".into());
4034 }
4035 (Some(_), None) if matches!(kind, Kind::Switch | Kind::Count(_)) => {
4036 return error("`default` needs a field that takes a value, or `negate`".into());
4037 }
4038 _ => {}
4039 }
4040 Ok(Field {
4041 ident,
4042 kind,
4043 role,
4044 delimiter,
4045 choices,
4046 env,
4047 default,
4048 conflicts,
4049 overrides,
4050 requires,
4051 required,
4052 required_unless,
4053 group,
4054 default_missing,
4055 long_help: long_help.or_else(|| help.clone()).unwrap_or(doc_long_help),
4056 help: help.unwrap_or(doc_help),
4057 heading,
4058 hide,
4059 value_name: if tristate { None } else { flag_value_name },
4060 negative_numbers,
4061 hyphen_values,
4062 require_equals,
4063 keep_equals,
4064 negate,
4065 two_dashes,
4066 prefix,
4067 values,
4068 values_max,
4069 value_terminator,
4070 default_fn,
4071 default_note,
4072 default_ifs,
4073 stop_flags,
4074 plus,
4075 tristate,
4076 keywords,
4077 short_aliases,
4078 })
4079}
4080
4081fn is_bool_word(word: &str) -> bool {
4082 matches!(word, "true" | "false")
4083}
4084
4085fn value_count(count: &syn::Expr) -> syn::Result<(usize, Option<usize>)> {
4088 let number = |e: &syn::Expr| match e {
4089 syn::Expr::Lit(syn::ExprLit {
4090 lit: syn::Lit::Int(n),
4091 ..
4092 }) => n.base10_parse::<usize>(),
4093 _ => Err(syn::Error::new(e.span(), "expected a number")),
4094 };
4095 let (min, max) = match count {
4096 syn::Expr::Range(range) => {
4097 let min = range.start.as_deref().map(number).transpose()?.unwrap_or(1);
4098 let max = match (&range.end, &range.limits) {
4099 (None, _) => usize::MAX,
4100 (Some(end), syn::RangeLimits::Closed(_)) => number(end)?,
4101 (Some(end), syn::RangeLimits::HalfOpen(_)) => number(end)?.saturating_sub(1),
4102 };
4103 (min, Some(max))
4104 }
4105 exact => (number(exact)?, None),
4106 };
4107 if min == 0 || max.is_some_and(|max| max < min) {
4108 return Err(syn::Error::new(
4109 count.span(),
4110 "`values` is at least 1, and a range does not end below its start",
4111 ));
4112 }
4113 Ok((min, max))
4114}
4115
4116fn spellings(f: &Field) -> Vec<&str> {
4118 let mut longs = f.longs();
4119 longs.extend(f.negate.as_deref());
4120 longs
4121}
4122
4123fn check_duplicates(fields: &[Field]) -> syn::Result<()> {
4124 for (i, a) in fields.iter().enumerate() {
4125 for b in &fields[..i] {
4126 let theirs = b.shorts();
4127 let clash = a
4128 .shorts()
4129 .into_iter()
4130 .find(|c| theirs.contains(c))
4131 .map(|c| format!("-{c}"))
4132 .or_else(|| {
4133 let longs = spellings(b);
4134 spellings(a)
4135 .into_iter()
4136 .find(|l| longs.contains(l))
4137 .map(|l| format!("--{l}"))
4138 });
4139 if let Some(name) = clash {
4140 return Err(syn::Error::new(
4141 a.ident.span(),
4142 format!("`{name}` is already used by `{}`", b.ident),
4143 ));
4144 }
4145 }
4146 }
4147 Ok(())
4148}
4149
4150fn check_positional_order(positionals: &[&Field]) -> syn::Result<()> {
4153 let rank = |f: &Field| match f.kind {
4154 Kind::Required(_) => 0,
4155 Kind::Optional(_) => 1,
4156 Kind::Many(_) | Kind::Switch | Kind::Count(_) => 2,
4157 };
4158 for pair in positionals.windows(2) {
4159 if rank(pair[1]) < rank(pair[0]) || rank(pair[0]) == 2 {
4160 return Err(syn::Error::new(
4161 pair[1].ident.span(),
4162 "positionals must be required, then optional, then one `Vec` last",
4163 ));
4164 }
4165 }
4166 Ok(())
4167}
4168
4169fn last_segment(ty: &Type) -> Option<&syn::PathSegment> {
4170 match ty {
4171 Type::Path(path) if path.qself.is_none() => path.path.segments.last(),
4172 _ => None,
4173 }
4174}
4175
4176fn inner(segment: &syn::PathSegment) -> Option<&Type> {
4177 match &segment.arguments {
4178 PathArguments::AngleBracketed(args) => match args.args.first()? {
4179 GenericArgument::Type(inner) => Some(inner),
4180 _ => None,
4181 },
4182 _ => None,
4183 }
4184}
4185
4186fn kind(ty: &Type) -> Kind {
4187 if is_bool(ty) {
4188 return Kind::Switch;
4189 }
4190 if let Some(last) = last_segment(ty) {
4191 if let Some(inner) = inner(last) {
4192 if last.ident == "Option" {
4193 return Kind::Optional(inner.clone());
4194 }
4195 if last.ident == "Vec" {
4196 return Kind::Many(inner.clone());
4197 }
4198 }
4199 }
4200 Kind::Required(ty.clone())
4201}