1 // Copyright 2018 Guillaume Pinot (@TeXitoi) <[email protected]>,
2 // Kevin Knapp (@kbknapp) <[email protected]>, and
3 // Ana Hobden (@hoverbear) <[email protected]>
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10 //
11 // This work was derived from Structopt (https://github.com/TeXitoi/structopt)
12 // commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
13 // MIT/Apache 2.0 license.
14 
15 use std::env;
16 
17 use heck::{ToKebabCase, ToLowerCamelCase, ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};
18 use proc_macro2::{self, Span, TokenStream};
19 use quote::{format_ident, quote, quote_spanned, ToTokens};
20 use syn::DeriveInput;
21 use syn::{self, ext::IdentExt, spanned::Spanned, Attribute, Field, Ident, LitStr, Type, Variant};
22 
23 use crate::attr::*;
24 use crate::utils::{extract_doc_comment, format_doc_comment, inner_type, is_simple_ty, Sp, Ty};
25 
26 /// Default casing style for generated arguments.
27 pub const DEFAULT_CASING: CasingStyle = CasingStyle::Kebab;
28 
29 /// Default casing style for environment variables
30 pub const DEFAULT_ENV_CASING: CasingStyle = CasingStyle::ScreamingSnake;
31 
32 #[derive(Clone)]
33 pub struct Item {
34     name: Name,
35     casing: Sp<CasingStyle>,
36     env_casing: Sp<CasingStyle>,
37     ty: Option<Type>,
38     doc_comment: Vec<Method>,
39     methods: Vec<Method>,
40     deprecations: Vec<Deprecation>,
41     value_parser: Option<ValueParser>,
42     action: Option<Action>,
43     verbatim_doc_comment: bool,
44     force_long_help: bool,
45     next_display_order: Option<Method>,
46     next_help_heading: Option<Method>,
47     is_enum: bool,
48     is_positional: bool,
49     skip_group: bool,
50     group_id: Name,
51     group_methods: Vec<Method>,
52     kind: Sp<Kind>,
53 }
54 
55 impl Item {
from_args_struct(input: &DeriveInput, name: Name) -> Result<Self, syn::Error>56     pub fn from_args_struct(input: &DeriveInput, name: Name) -> Result<Self, syn::Error> {
57         let ident = input.ident.clone();
58         let span = input.ident.span();
59         let attrs = &input.attrs;
60         let argument_casing = Sp::new(DEFAULT_CASING, span);
61         let env_casing = Sp::new(DEFAULT_ENV_CASING, span);
62         let kind = Sp::new(Kind::Command(Sp::new(Ty::Other, span)), span);
63 
64         let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind);
65         let parsed_attrs = ClapAttr::parse_all(attrs)?;
66         res.infer_kind(&parsed_attrs)?;
67         res.push_attrs(&parsed_attrs)?;
68         res.push_doc_comment(attrs, "about", Some("long_about"));
69 
70         Ok(res)
71     }
72 
from_subcommand_enum(input: &DeriveInput, name: Name) -> Result<Self, syn::Error>73     pub fn from_subcommand_enum(input: &DeriveInput, name: Name) -> Result<Self, syn::Error> {
74         let ident = input.ident.clone();
75         let span = input.ident.span();
76         let attrs = &input.attrs;
77         let argument_casing = Sp::new(DEFAULT_CASING, span);
78         let env_casing = Sp::new(DEFAULT_ENV_CASING, span);
79         let kind = Sp::new(Kind::Command(Sp::new(Ty::Other, span)), span);
80 
81         let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind);
82         let parsed_attrs = ClapAttr::parse_all(attrs)?;
83         res.infer_kind(&parsed_attrs)?;
84         res.push_attrs(&parsed_attrs)?;
85         res.push_doc_comment(attrs, "about", Some("long_about"));
86 
87         Ok(res)
88     }
89 
from_value_enum(input: &DeriveInput, name: Name) -> Result<Self, syn::Error>90     pub fn from_value_enum(input: &DeriveInput, name: Name) -> Result<Self, syn::Error> {
91         let ident = input.ident.clone();
92         let span = input.ident.span();
93         let attrs = &input.attrs;
94         let argument_casing = Sp::new(DEFAULT_CASING, span);
95         let env_casing = Sp::new(DEFAULT_ENV_CASING, span);
96         let kind = Sp::new(Kind::Value, span);
97 
98         let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind);
99         let parsed_attrs = ClapAttr::parse_all(attrs)?;
100         res.infer_kind(&parsed_attrs)?;
101         res.push_attrs(&parsed_attrs)?;
102         // Ignoring `push_doc_comment` as there is no top-level clap builder to add documentation
103         // to
104 
105         if res.has_explicit_methods() {
106             abort!(
107                 res.methods[0].name.span(),
108                 "{} doesn't exist for `ValueEnum` enums",
109                 res.methods[0].name
110             );
111         }
112 
113         Ok(res)
114     }
115 
from_subcommand_variant( variant: &Variant, struct_casing: Sp<CasingStyle>, env_casing: Sp<CasingStyle>, ) -> Result<Self, syn::Error>116     pub fn from_subcommand_variant(
117         variant: &Variant,
118         struct_casing: Sp<CasingStyle>,
119         env_casing: Sp<CasingStyle>,
120     ) -> Result<Self, syn::Error> {
121         let name = variant.ident.clone();
122         let ident = variant.ident.clone();
123         let span = variant.span();
124         let ty = match variant.fields {
125             syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
126                 Ty::from_syn_ty(&unnamed[0].ty)
127             }
128             syn::Fields::Named(_) | syn::Fields::Unnamed(..) | syn::Fields::Unit => {
129                 Sp::new(Ty::Other, span)
130             }
131         };
132         let kind = Sp::new(Kind::Command(ty), span);
133         let mut res = Self::new(
134             Name::Derived(name),
135             ident,
136             None,
137             struct_casing,
138             env_casing,
139             kind,
140         );
141         let parsed_attrs = ClapAttr::parse_all(&variant.attrs)?;
142         res.infer_kind(&parsed_attrs)?;
143         res.push_attrs(&parsed_attrs)?;
144         if matches!(&*res.kind, Kind::Command(_) | Kind::Subcommand(_)) {
145             res.push_doc_comment(&variant.attrs, "about", Some("long_about"));
146         }
147 
148         match &*res.kind {
149             Kind::Flatten(_) => {
150                 if res.has_explicit_methods() {
151                     abort!(
152                         res.kind.span(),
153                         "methods are not allowed for flattened entry"
154                     );
155                 }
156             }
157 
158             Kind::Subcommand(_)
159             | Kind::ExternalSubcommand
160             | Kind::FromGlobal(_)
161             | Kind::Skip(_, _)
162             | Kind::Command(_)
163             | Kind::Value
164             | Kind::Arg(_) => (),
165         }
166 
167         Ok(res)
168     }
169 
from_value_enum_variant( variant: &Variant, argument_casing: Sp<CasingStyle>, env_casing: Sp<CasingStyle>, ) -> Result<Self, syn::Error>170     pub fn from_value_enum_variant(
171         variant: &Variant,
172         argument_casing: Sp<CasingStyle>,
173         env_casing: Sp<CasingStyle>,
174     ) -> Result<Self, syn::Error> {
175         let ident = variant.ident.clone();
176         let span = variant.span();
177         let kind = Sp::new(Kind::Value, span);
178         let mut res = Self::new(
179             Name::Derived(variant.ident.clone()),
180             ident,
181             None,
182             argument_casing,
183             env_casing,
184             kind,
185         );
186         let parsed_attrs = ClapAttr::parse_all(&variant.attrs)?;
187         res.infer_kind(&parsed_attrs)?;
188         res.push_attrs(&parsed_attrs)?;
189         if matches!(&*res.kind, Kind::Value) {
190             res.push_doc_comment(&variant.attrs, "help", None);
191         }
192 
193         Ok(res)
194     }
195 
from_args_field( field: &Field, struct_casing: Sp<CasingStyle>, env_casing: Sp<CasingStyle>, ) -> Result<Self, syn::Error>196     pub fn from_args_field(
197         field: &Field,
198         struct_casing: Sp<CasingStyle>,
199         env_casing: Sp<CasingStyle>,
200     ) -> Result<Self, syn::Error> {
201         let name = field.ident.clone().unwrap();
202         let ident = field.ident.clone().unwrap();
203         let span = field.span();
204         let ty = Ty::from_syn_ty(&field.ty);
205         let kind = Sp::new(Kind::Arg(ty), span);
206         let mut res = Self::new(
207             Name::Derived(name),
208             ident,
209             Some(field.ty.clone()),
210             struct_casing,
211             env_casing,
212             kind,
213         );
214         let parsed_attrs = ClapAttr::parse_all(&field.attrs)?;
215         res.infer_kind(&parsed_attrs)?;
216         res.push_attrs(&parsed_attrs)?;
217         if matches!(&*res.kind, Kind::Arg(_)) {
218             res.push_doc_comment(&field.attrs, "help", Some("long_help"));
219         }
220 
221         match &*res.kind {
222             Kind::Flatten(_) => {
223                 if res.has_explicit_methods() {
224                     abort!(
225                         res.kind.span(),
226                         "methods are not allowed for flattened entry"
227                     );
228                 }
229             }
230 
231             Kind::Subcommand(_) => {
232                 if res.has_explicit_methods() {
233                     abort!(
234                         res.kind.span(),
235                         "methods in attributes are not allowed for subcommand"
236                     );
237                 }
238             }
239             Kind::Skip(_, _)
240             | Kind::FromGlobal(_)
241             | Kind::Arg(_)
242             | Kind::Command(_)
243             | Kind::Value
244             | Kind::ExternalSubcommand => {}
245         }
246 
247         Ok(res)
248     }
249 
new( name: Name, ident: Ident, ty: Option<Type>, casing: Sp<CasingStyle>, env_casing: Sp<CasingStyle>, kind: Sp<Kind>, ) -> Self250     fn new(
251         name: Name,
252         ident: Ident,
253         ty: Option<Type>,
254         casing: Sp<CasingStyle>,
255         env_casing: Sp<CasingStyle>,
256         kind: Sp<Kind>,
257     ) -> Self {
258         let group_id = Name::Derived(ident);
259         Self {
260             name,
261             ty,
262             casing,
263             env_casing,
264             doc_comment: vec![],
265             methods: vec![],
266             deprecations: vec![],
267             value_parser: None,
268             action: None,
269             verbatim_doc_comment: false,
270             force_long_help: false,
271             next_display_order: None,
272             next_help_heading: None,
273             is_enum: false,
274             is_positional: true,
275             skip_group: false,
276             group_id,
277             group_methods: vec![],
278             kind,
279         }
280     }
281 
push_method(&mut self, kind: AttrKind, name: Ident, arg: impl ToTokens)282     fn push_method(&mut self, kind: AttrKind, name: Ident, arg: impl ToTokens) {
283         self.push_method_(kind, name, arg.to_token_stream());
284     }
285 
push_method_(&mut self, kind: AttrKind, name: Ident, arg: TokenStream)286     fn push_method_(&mut self, kind: AttrKind, name: Ident, arg: TokenStream) {
287         if name == "id" {
288             match kind {
289                 AttrKind::Command | AttrKind::Value => {
290                     self.deprecations.push(Deprecation {
291                         span: name.span(),
292                         id: "id_is_only_for_arg",
293                         version: "4.0.0",
294                         description: format!(
295                             "`#[{}(id)] was allowed by mistake, instead use `#[{}(name)]`",
296                             kind.as_str(),
297                             kind.as_str()
298                         ),
299                     });
300                     self.name = Name::Assigned(arg);
301                 }
302                 AttrKind::Group => {
303                     self.group_id = Name::Assigned(arg);
304                 }
305                 AttrKind::Arg | AttrKind::Clap | AttrKind::StructOpt => {
306                     self.name = Name::Assigned(arg);
307                 }
308             }
309         } else if name == "name" {
310             match kind {
311                 AttrKind::Arg => {
312                     self.deprecations.push(Deprecation {
313                         span: name.span(),
314                         id: "id_is_only_for_arg",
315                         version: "4.0.0",
316                         description: format!(
317                             "`#[{}(name)] was allowed by mistake, instead use `#[{}(id)]` or `#[{}(value_name)]`",
318                             kind.as_str(),
319                             kind.as_str(),
320                             kind.as_str()
321                         ),
322                     });
323                     self.name = Name::Assigned(arg);
324                 }
325                 AttrKind::Group => self.group_methods.push(Method::new(name, arg)),
326                 AttrKind::Command | AttrKind::Value | AttrKind::Clap | AttrKind::StructOpt => {
327                     self.name = Name::Assigned(arg);
328                 }
329             }
330         } else if name == "value_parser" {
331             self.value_parser = Some(ValueParser::Explicit(Method::new(name, arg)));
332         } else if name == "action" {
333             self.action = Some(Action::Explicit(Method::new(name, arg)));
334         } else {
335             if name == "short" || name == "long" {
336                 self.is_positional = false;
337             }
338             match kind {
339                 AttrKind::Group => self.group_methods.push(Method::new(name, arg)),
340                 _ => self.methods.push(Method::new(name, arg)),
341             };
342         }
343     }
344 
infer_kind(&mut self, attrs: &[ClapAttr]) -> Result<(), syn::Error>345     fn infer_kind(&mut self, attrs: &[ClapAttr]) -> Result<(), syn::Error> {
346         for attr in attrs {
347             if let Some(AttrValue::Call(_)) = &attr.value {
348                 continue;
349             }
350 
351             let actual_attr_kind = *attr.kind.get();
352             let kind = match &attr.magic {
353                 Some(MagicAttrName::FromGlobal) => {
354                     if attr.value.is_some() {
355                         let expr = attr.value_or_abort()?;
356                         abort!(expr, "attribute `{}` does not accept a value", attr.name);
357                     }
358                     let ty = self
359                         .kind()
360                         .ty()
361                         .cloned()
362                         .unwrap_or_else(|| Sp::new(Ty::Other, self.kind.span()));
363                     let kind = Sp::new(Kind::FromGlobal(ty), attr.name.clone().span());
364                     Some(kind)
365                 }
366                 Some(MagicAttrName::Subcommand) if attr.value.is_none() => {
367                     if attr.value.is_some() {
368                         let expr = attr.value_or_abort()?;
369                         abort!(expr, "attribute `{}` does not accept a value", attr.name);
370                     }
371                     let ty = self
372                         .kind()
373                         .ty()
374                         .cloned()
375                         .unwrap_or_else(|| Sp::new(Ty::Other, self.kind.span()));
376                     let kind = Sp::new(Kind::Subcommand(ty), attr.name.clone().span());
377                     Some(kind)
378                 }
379                 Some(MagicAttrName::ExternalSubcommand) if attr.value.is_none() => {
380                     if attr.value.is_some() {
381                         let expr = attr.value_or_abort()?;
382                         abort!(expr, "attribute `{}` does not accept a value", attr.name);
383                     }
384                     let kind = Sp::new(Kind::ExternalSubcommand, attr.name.clone().span());
385                     Some(kind)
386                 }
387                 Some(MagicAttrName::Flatten) if attr.value.is_none() => {
388                     if attr.value.is_some() {
389                         let expr = attr.value_or_abort()?;
390                         abort!(expr, "attribute `{}` does not accept a value", attr.name);
391                     }
392                     let ty = self
393                         .kind()
394                         .ty()
395                         .cloned()
396                         .unwrap_or_else(|| Sp::new(Ty::Other, self.kind.span()));
397                     let kind = Sp::new(Kind::Flatten(ty), attr.name.clone().span());
398                     Some(kind)
399                 }
400                 Some(MagicAttrName::Skip) if actual_attr_kind != AttrKind::Group => {
401                     let expr = attr.value.clone();
402                     let kind = Sp::new(
403                         Kind::Skip(expr, self.kind.attr_kind()),
404                         attr.name.clone().span(),
405                     );
406                     Some(kind)
407                 }
408                 _ => None,
409             };
410 
411             if let Some(kind) = kind {
412                 self.set_kind(kind)?;
413             }
414         }
415 
416         Ok(())
417     }
418 
push_attrs(&mut self, attrs: &[ClapAttr]) -> Result<(), syn::Error>419     fn push_attrs(&mut self, attrs: &[ClapAttr]) -> Result<(), syn::Error> {
420         for attr in attrs {
421             let actual_attr_kind = *attr.kind.get();
422             let expected_attr_kind = self.kind.attr_kind();
423             match (actual_attr_kind, expected_attr_kind) {
424                 (AttrKind::Clap, _) | (AttrKind::StructOpt, _) => {
425                     self.deprecations.push(Deprecation::attribute(
426                         "4.0.0",
427                         actual_attr_kind,
428                         expected_attr_kind,
429                         attr.kind.span(),
430                     ));
431                 }
432 
433                 (AttrKind::Group, AttrKind::Command) => {}
434 
435                 _ if attr.kind != expected_attr_kind => {
436                     abort!(
437                         attr.kind.span(),
438                         "Expected `{}` attribute instead of `{}`",
439                         expected_attr_kind.as_str(),
440                         actual_attr_kind.as_str()
441                     );
442                 }
443 
444                 _ => {}
445             }
446 
447             if let Some(AttrValue::Call(tokens)) = &attr.value {
448                 // Force raw mode with method call syntax
449                 self.push_method(*attr.kind.get(), attr.name.clone(), quote!(#(#tokens),*));
450                 continue;
451             }
452 
453             match &attr.magic {
454                 Some(MagicAttrName::Short) if attr.value.is_none() => {
455                     assert_attr_kind(attr, &[AttrKind::Arg])?;
456 
457                     self.push_method(
458                         *attr.kind.get(),
459                         attr.name.clone(),
460                         self.name.clone().translate_char(*self.casing),
461                     );
462                 }
463 
464                 Some(MagicAttrName::Long) if attr.value.is_none() => {
465                     assert_attr_kind(attr, &[AttrKind::Arg])?;
466 
467                     self.push_method(*attr.kind.get(), attr.name.clone(), self.name.clone().translate(*self.casing));
468                 }
469 
470                 Some(MagicAttrName::ValueParser) if attr.value.is_none() => {
471                     assert_attr_kind(attr, &[AttrKind::Arg])?;
472 
473                     self.deprecations.push(Deprecation {
474                         span: attr.name.span(),
475                         id: "bare_value_parser",
476                         version: "4.0.0",
477                         description: "`#[arg(value_parser)]` is now the default and is no longer needed`".to_owned(),
478                     });
479                     self.value_parser = Some(ValueParser::Implicit(attr.name.clone()));
480                 }
481 
482                 Some(MagicAttrName::Action) if attr.value.is_none() => {
483                     assert_attr_kind(attr, &[AttrKind::Arg])?;
484 
485                     self.deprecations.push(Deprecation {
486                         span: attr.name.span(),
487                         id: "bare_action",
488                         version: "4.0.0",
489                         description: "`#[arg(action)]` is now the default and is no longer needed`".to_owned(),
490                     });
491                     self.action = Some(Action::Implicit(attr.name.clone()));
492                 }
493 
494                 Some(MagicAttrName::Env) if attr.value.is_none() => {
495                     assert_attr_kind(attr, &[AttrKind::Arg])?;
496 
497                     self.push_method(
498                         *attr.kind.get(),
499                         attr.name.clone(),
500                         self.name.clone().translate(*self.env_casing),
501                     );
502                 }
503 
504                 Some(MagicAttrName::ValueEnum) if attr.value.is_none() => {
505                     assert_attr_kind(attr, &[AttrKind::Arg])?;
506 
507                     self.is_enum = true
508                 }
509 
510                 Some(MagicAttrName::VerbatimDocComment) if attr.value.is_none() => {
511                     self.verbatim_doc_comment = true
512                 }
513 
514                 Some(MagicAttrName::About) if attr.value.is_none() => {
515                     assert_attr_kind(attr, &[AttrKind::Command])?;
516 
517                     if let Some(method) =
518                         Method::from_env(attr.name.clone(), "CARGO_PKG_DESCRIPTION")?
519                     {
520                         self.methods.push(method);
521                     }
522                 }
523 
524                 Some(MagicAttrName::LongAbout) if attr.value.is_none() => {
525                     assert_attr_kind(attr, &[AttrKind::Command])?;
526 
527                     self.force_long_help = true;
528                 }
529 
530                 Some(MagicAttrName::LongHelp) if attr.value.is_none() => {
531                     assert_attr_kind(attr, &[AttrKind::Arg])?;
532 
533                     self.force_long_help = true;
534                 }
535 
536                 Some(MagicAttrName::Author) if attr.value.is_none() => {
537                     assert_attr_kind(attr, &[AttrKind::Command])?;
538 
539                     if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_AUTHORS")? {
540                         self.methods.push(method);
541                     }
542                 }
543 
544                 Some(MagicAttrName::Version) if attr.value.is_none() => {
545                     assert_attr_kind(attr, &[AttrKind::Command])?;
546 
547                     if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_VERSION")? {
548                         self.methods.push(method);
549                     }
550                 }
551 
552                 Some(MagicAttrName::DefaultValueT) => {
553                     assert_attr_kind(attr, &[AttrKind::Arg])?;
554 
555                     let ty = if let Some(ty) = self.ty.as_ref() {
556                         ty
557                     } else {
558                         abort!(
559                             attr.name.clone(),
560                             "#[arg(default_value_t)] (without an argument) can be used \
561                             only on field level\n\n= note: {note}\n\n",
562 
563                             note = "see \
564                                 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
565                     };
566 
567                     let val = if let Some(expr) = &attr.value {
568                         quote!(#expr)
569                     } else {
570                         quote!(<#ty as ::std::default::Default>::default())
571                     };
572 
573                     let val = if attrs
574                         .iter()
575                         .any(|a| a.magic == Some(MagicAttrName::ValueEnum))
576                     {
577                         quote_spanned!(attr.name.clone().span()=> {
578                             static DEFAULT_VALUE: ::std::sync::OnceLock<String> = ::std::sync::OnceLock::new();
579                             let s = DEFAULT_VALUE.get_or_init(|| {
580                                 let val: #ty = #val;
581                                 clap::ValueEnum::to_possible_value(&val).unwrap().get_name().to_owned()
582                             });
583                             let s: &'static str = &*s;
584                             s
585                         })
586                     } else {
587                         quote_spanned!(attr.name.clone().span()=> {
588                             static DEFAULT_VALUE: ::std::sync::OnceLock<String> = ::std::sync::OnceLock::new();
589                             let s = DEFAULT_VALUE.get_or_init(|| {
590                                 let val: #ty = #val;
591                                 ::std::string::ToString::to_string(&val)
592                             });
593                             let s: &'static str = &*s;
594                             s
595                         })
596                     };
597 
598                     let raw_ident = Ident::new("default_value", attr.name.clone().span());
599                     self.methods.push(Method::new(raw_ident, val));
600                 }
601 
602                 Some(MagicAttrName::DefaultValuesT) => {
603                     assert_attr_kind(attr, &[AttrKind::Arg])?;
604 
605                     let ty = if let Some(ty) = self.ty.as_ref() {
606                         ty
607                     } else {
608                         abort!(
609                             attr.name.clone(),
610                             "#[arg(default_values_t)] (without an argument) can be used \
611                             only on field level\n\n= note: {note}\n\n",
612 
613                             note = "see \
614                                 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
615                     };
616                     let expr = attr.value_or_abort()?;
617 
618                     let container_type = Ty::from_syn_ty(ty);
619                     if *container_type != Ty::Vec {
620                         abort!(
621                             attr.name.clone(),
622                             "#[arg(default_values_t)] can be used only on Vec types\n\n= note: {note}\n\n",
623 
624                             note = "see \
625                                 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
626                     }
627                     let inner_type = inner_type(ty);
628 
629                     // Use `Borrow<#inner_type>` so we accept `&Vec<#inner_type>` and
630                     // `Vec<#inner_type>`.
631                     let val = if attrs
632                         .iter()
633                         .any(|a| a.magic == Some(MagicAttrName::ValueEnum))
634                     {
635                         quote_spanned!(attr.name.clone().span()=> {
636                             {
637                                 fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=String>
638                                 where
639                                     T: ::std::borrow::Borrow<#inner_type>
640                                 {
641                                     iterable
642                                         .into_iter()
643                                         .map(|val| {
644                                             clap::ValueEnum::to_possible_value(val.borrow()).unwrap().get_name().to_owned()
645                                         })
646                                 }
647 
648                                 static DEFAULT_STRINGS: ::std::sync::OnceLock<Vec<String>> = ::std::sync::OnceLock::new();
649                                 static DEFAULT_VALUES: ::std::sync::OnceLock<Vec<&str>> = ::std::sync::OnceLock::new();
650                                 DEFAULT_VALUES.get_or_init(|| {
651                                     DEFAULT_STRINGS.get_or_init(|| iter_to_vals(#expr).collect()).iter().map(::std::string::String::as_str).collect()
652                                 }).iter().copied()
653                             }
654                         })
655                     } else {
656                         quote_spanned!(attr.name.clone().span()=> {
657                             {
658                                 fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=String>
659                                 where
660                                     T: ::std::borrow::Borrow<#inner_type>
661                                 {
662                                     iterable.into_iter().map(|val| val.borrow().to_string())
663                                 }
664 
665                                 static DEFAULT_STRINGS: ::std::sync::OnceLock<Vec<String>> = ::std::sync::OnceLock::new();
666                                 static DEFAULT_VALUES: ::std::sync::OnceLock<Vec<&str>> = ::std::sync::OnceLock::new();
667                                 DEFAULT_VALUES.get_or_init(|| {
668                                     DEFAULT_STRINGS.get_or_init(|| iter_to_vals(#expr).collect()).iter().map(::std::string::String::as_str).collect()
669                                 }).iter().copied()
670                             }
671                         })
672                     };
673 
674                     self.methods.push(Method::new(
675                         Ident::new("default_values", attr.name.clone().span()),
676                         val,
677                     ));
678                 }
679 
680                 Some(MagicAttrName::DefaultValueOsT) => {
681                     assert_attr_kind(attr, &[AttrKind::Arg])?;
682 
683                     let ty = if let Some(ty) = self.ty.as_ref() {
684                         ty
685                     } else {
686                         abort!(
687                             attr.name.clone(),
688                             "#[arg(default_value_os_t)] (without an argument) can be used \
689                             only on field level\n\n= note: {note}\n\n",
690 
691                             note = "see \
692                                 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
693                     };
694 
695                     let val = if let Some(expr) = &attr.value {
696                         quote!(#expr)
697                     } else {
698                         quote!(<#ty as ::std::default::Default>::default())
699                     };
700 
701                     let val = if attrs
702                         .iter()
703                         .any(|a| a.magic == Some(MagicAttrName::ValueEnum))
704                     {
705                         quote_spanned!(attr.name.clone().span()=> {
706                             static DEFAULT_VALUE: ::std::sync::OnceLock<String> = ::std::sync::OnceLock::new();
707                             let s = DEFAULT_VALUE.get_or_init(|| {
708                                 let val: #ty = #val;
709                                 clap::ValueEnum::to_possible_value(&val).unwrap().get_name().to_owned()
710                             });
711                             let s: &'static str = &*s;
712                             s
713                         })
714                     } else {
715                         quote_spanned!(attr.name.clone().span()=> {
716                             static DEFAULT_VALUE: ::std::sync::OnceLock<::std::ffi::OsString> = ::std::sync::OnceLock::new();
717                             let s = DEFAULT_VALUE.get_or_init(|| {
718                                 let val: #ty = #val;
719                                 ::std::ffi::OsString::from(val)
720                             });
721                             let s: &'static ::std::ffi::OsStr = &*s;
722                             s
723                         })
724                     };
725 
726                     let raw_ident = Ident::new("default_value", attr.name.clone().span());
727                     self.methods.push(Method::new(raw_ident, val));
728                 }
729 
730                 Some(MagicAttrName::DefaultValuesOsT) => {
731                     assert_attr_kind(attr, &[AttrKind::Arg])?;
732 
733                     let ty = if let Some(ty) = self.ty.as_ref() {
734                         ty
735                     } else {
736                         abort!(
737                             attr.name.clone(),
738                             "#[arg(default_values_os_t)] (without an argument) can be used \
739                             only on field level\n\n= note: {note}\n\n",
740 
741                             note = "see \
742                                 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
743                     };
744                     let expr = attr.value_or_abort()?;
745 
746                     let container_type = Ty::from_syn_ty(ty);
747                     if *container_type != Ty::Vec {
748                         abort!(
749                             attr.name.clone(),
750                             "#[arg(default_values_os_t)] can be used only on Vec types\n\n= note: {note}\n\n",
751 
752                             note = "see \
753                                 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
754                     }
755                     let inner_type = inner_type(ty);
756 
757                     // Use `Borrow<#inner_type>` so we accept `&Vec<#inner_type>` and
758                     // `Vec<#inner_type>`.
759                     let val = if attrs
760                         .iter()
761                         .any(|a| a.magic == Some(MagicAttrName::ValueEnum))
762                     {
763                         quote_spanned!(attr.name.clone().span()=> {
764                             {
765                                 fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=::std::ffi::OsString>
766                                 where
767                                     T: ::std::borrow::Borrow<#inner_type>
768                                 {
769                                     iterable
770                                         .into_iter()
771                                         .map(|val| {
772                                             clap::ValueEnum::to_possible_value(val.borrow()).unwrap().get_name().to_owned().into()
773                                         })
774                                 }
775 
776                                 static DEFAULT_STRINGS: ::std::sync::OnceLock<Vec<::std::ffi::OsString>> = ::std::sync::OnceLock::new();
777                                 static DEFAULT_VALUES: ::std::sync::OnceLock<Vec<&::std::ffi::OsStr>> = ::std::sync::OnceLock::new();
778                                 DEFAULT_VALUES.get_or_init(|| {
779                                     DEFAULT_STRINGS.get_or_init(|| iter_to_vals(#expr).collect()).iter().map(::std::ffi::OsString::as_os_str).collect()
780                                 }).iter().copied()
781                             }
782                         })
783                     } else {
784                         quote_spanned!(attr.name.clone().span()=> {
785                             {
786                                 fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=::std::ffi::OsString>
787                                 where
788                                     T: ::std::borrow::Borrow<#inner_type>
789                                 {
790                                     iterable.into_iter().map(|val| val.borrow().into())
791                                 }
792 
793                                 static DEFAULT_STRINGS: ::std::sync::OnceLock<Vec<::std::ffi::OsString>> = ::std::sync::OnceLock::new();
794                                 static DEFAULT_VALUES: ::std::sync::OnceLock<Vec<&::std::ffi::OsStr>> = ::std::sync::OnceLock::new();
795                                 DEFAULT_VALUES.get_or_init(|| {
796                                     DEFAULT_STRINGS.get_or_init(|| iter_to_vals(#expr).collect()).iter().map(::std::ffi::OsString::as_os_str).collect()
797                                 }).iter().copied()
798                             }
799                         })
800                     };
801 
802                     self.methods.push(Method::new(
803                         Ident::new("default_values", attr.name.clone().span()),
804                         val,
805                     ));
806                 }
807 
808                 Some(MagicAttrName::NextDisplayOrder) => {
809                     assert_attr_kind(attr, &[AttrKind::Command])?;
810 
811                     let expr = attr.value_or_abort()?;
812                     self.next_display_order = Some(Method::new(attr.name.clone(), quote!(#expr)));
813                 }
814 
815                 Some(MagicAttrName::NextHelpHeading) => {
816                     assert_attr_kind(attr, &[AttrKind::Command])?;
817 
818                     let expr = attr.value_or_abort()?;
819                     self.next_help_heading = Some(Method::new(attr.name.clone(), quote!(#expr)));
820                 }
821 
822                 Some(MagicAttrName::RenameAll) => {
823                     let lit = attr.lit_str_or_abort()?;
824                     self.casing = CasingStyle::from_lit(lit)?;
825                 }
826 
827                 Some(MagicAttrName::RenameAllEnv) => {
828                     assert_attr_kind(attr, &[AttrKind::Command, AttrKind::Arg])?;
829 
830                     let lit = attr.lit_str_or_abort()?;
831                     self.env_casing = CasingStyle::from_lit(lit)?;
832                 }
833 
834                 Some(MagicAttrName::Skip) if actual_attr_kind == AttrKind::Group => {
835                     self.skip_group = true;
836                 }
837 
838                 None
839                 // Magic only for the default, otherwise just forward to the builder
840                 | Some(MagicAttrName::Short)
841                 | Some(MagicAttrName::Long)
842                 | Some(MagicAttrName::Env)
843                 | Some(MagicAttrName::About)
844                 | Some(MagicAttrName::LongAbout)
845                 | Some(MagicAttrName::LongHelp)
846                 | Some(MagicAttrName::Author)
847                 | Some(MagicAttrName::Version)
848                  => {
849                     let expr = attr.value_or_abort()?;
850                     self.push_method(*attr.kind.get(), attr.name.clone(), expr);
851                 }
852 
853                 // Magic only for the default, otherwise just forward to the builder
854                 Some(MagicAttrName::ValueParser) | Some(MagicAttrName::Action) => {
855                     let expr = attr.value_or_abort()?;
856                     self.push_method(*attr.kind.get(), attr.name.clone(), expr);
857                 }
858 
859                 // Directives that never receive a value
860                 Some(MagicAttrName::ValueEnum)
861                 | Some(MagicAttrName::VerbatimDocComment) => {
862                     let expr = attr.value_or_abort()?;
863                     abort!(expr, "attribute `{}` does not accept a value", attr.name);
864                 }
865 
866                 // Kinds
867                 Some(MagicAttrName::FromGlobal)
868                 | Some(MagicAttrName::Subcommand)
869                 | Some(MagicAttrName::ExternalSubcommand)
870                 | Some(MagicAttrName::Flatten)
871                 | Some(MagicAttrName::Skip) => {
872                 }
873             }
874         }
875 
876         if self.has_explicit_methods() {
877             if let Kind::Skip(_, attr) = &*self.kind {
878                 abort!(
879                     self.methods[0].name.span(),
880                     "`{}` cannot be used with `#[{}(skip)]",
881                     self.methods[0].name,
882                     attr.as_str(),
883                 );
884             }
885             if let Kind::FromGlobal(_) = &*self.kind {
886                 abort!(
887                     self.methods[0].name.span(),
888                     "`{}` cannot be used with `#[arg(from_global)]",
889                     self.methods[0].name,
890                 );
891             }
892         }
893 
894         Ok(())
895     }
896 
push_doc_comment(&mut self, attrs: &[Attribute], short_name: &str, long_name: Option<&str>)897     fn push_doc_comment(&mut self, attrs: &[Attribute], short_name: &str, long_name: Option<&str>) {
898         let lines = extract_doc_comment(attrs);
899 
900         if !lines.is_empty() {
901             let (short_help, long_help) =
902                 format_doc_comment(&lines, !self.verbatim_doc_comment, self.force_long_help);
903             let short_name = format_ident!("{short_name}");
904             let short = Method::new(
905                 short_name,
906                 short_help
907                     .map(|h| quote!(#h))
908                     .unwrap_or_else(|| quote!(None)),
909             );
910             self.doc_comment.push(short);
911             if let Some(long_name) = long_name {
912                 let long_name = format_ident!("{long_name}");
913                 let long = Method::new(
914                     long_name,
915                     long_help
916                         .map(|h| quote!(#h))
917                         .unwrap_or_else(|| quote!(None)),
918                 );
919                 self.doc_comment.push(long);
920             }
921         }
922     }
923 
set_kind(&mut self, kind: Sp<Kind>) -> Result<(), syn::Error>924     fn set_kind(&mut self, kind: Sp<Kind>) -> Result<(), syn::Error> {
925         match (self.kind.get(), kind.get()) {
926             (Kind::Arg(_), Kind::FromGlobal(_))
927             | (Kind::Arg(_), Kind::Subcommand(_))
928             | (Kind::Arg(_), Kind::Flatten(_))
929             | (Kind::Arg(_), Kind::Skip(_, _))
930             | (Kind::Command(_), Kind::Subcommand(_))
931             | (Kind::Command(_), Kind::Flatten(_))
932             | (Kind::Command(_), Kind::Skip(_, _))
933             | (Kind::Command(_), Kind::ExternalSubcommand)
934             | (Kind::Value, Kind::Skip(_, _)) => {
935                 self.kind = kind;
936             }
937 
938             (_, _) => {
939                 let old = self.kind.name();
940                 let new = kind.name();
941                 abort!(kind.span(), "`{new}` cannot be used with `{old}`");
942             }
943         }
944         Ok(())
945     }
946 
find_default_method(&self) -> Option<&Method>947     pub fn find_default_method(&self) -> Option<&Method> {
948         self.methods
949             .iter()
950             .find(|m| m.name == "default_value" || m.name == "default_value_os")
951     }
952 
953     /// generate methods from attributes on top of struct or enum
initial_top_level_methods(&self) -> TokenStream954     pub fn initial_top_level_methods(&self) -> TokenStream {
955         let next_display_order = self.next_display_order.as_ref().into_iter();
956         let next_help_heading = self.next_help_heading.as_ref().into_iter();
957         quote!(
958             #(#next_display_order)*
959             #(#next_help_heading)*
960         )
961     }
962 
final_top_level_methods(&self) -> TokenStream963     pub fn final_top_level_methods(&self) -> TokenStream {
964         let methods = &self.methods;
965         let doc_comment = &self.doc_comment;
966 
967         quote!( #(#doc_comment)* #(#methods)*)
968     }
969 
970     /// generate methods on top of a field
field_methods(&self) -> proc_macro2::TokenStream971     pub fn field_methods(&self) -> proc_macro2::TokenStream {
972         let methods = &self.methods;
973         let doc_comment = &self.doc_comment;
974         quote!( #(#doc_comment)* #(#methods)* )
975     }
976 
group_id(&self) -> TokenStream977     pub fn group_id(&self) -> TokenStream {
978         self.group_id.clone().raw()
979     }
980 
group_methods(&self) -> TokenStream981     pub fn group_methods(&self) -> TokenStream {
982         let group_methods = &self.group_methods;
983         quote!( #(#group_methods)* )
984     }
985 
deprecations(&self) -> proc_macro2::TokenStream986     pub fn deprecations(&self) -> proc_macro2::TokenStream {
987         let deprecations = &self.deprecations;
988         quote!( #(#deprecations)* )
989     }
990 
next_display_order(&self) -> TokenStream991     pub fn next_display_order(&self) -> TokenStream {
992         let next_display_order = self.next_display_order.as_ref().into_iter();
993         quote!( #(#next_display_order)* )
994     }
995 
next_help_heading(&self) -> TokenStream996     pub fn next_help_heading(&self) -> TokenStream {
997         let next_help_heading = self.next_help_heading.as_ref().into_iter();
998         quote!( #(#next_help_heading)* )
999     }
1000 
id(&self) -> TokenStream1001     pub fn id(&self) -> TokenStream {
1002         self.name.clone().raw()
1003     }
1004 
cased_name(&self) -> TokenStream1005     pub fn cased_name(&self) -> TokenStream {
1006         self.name.clone().translate(*self.casing)
1007     }
1008 
value_name(&self) -> TokenStream1009     pub fn value_name(&self) -> TokenStream {
1010         self.name.clone().translate(CasingStyle::ScreamingSnake)
1011     }
1012 
value_parser(&self, field_type: &Type) -> Method1013     pub fn value_parser(&self, field_type: &Type) -> Method {
1014         self.value_parser
1015             .clone()
1016             .map(|p| {
1017                 let inner_type = inner_type(field_type);
1018                 p.resolve(inner_type)
1019             })
1020             .unwrap_or_else(|| {
1021                 let inner_type = inner_type(field_type);
1022                 if let Some(action) = self.action.as_ref() {
1023                     let span = action.span();
1024                     default_value_parser(inner_type, span)
1025                 } else {
1026                     let span = self
1027                         .action
1028                         .as_ref()
1029                         .map(|a| a.span())
1030                         .unwrap_or_else(|| self.kind.span());
1031                     default_value_parser(inner_type, span)
1032                 }
1033             })
1034     }
1035 
action(&self, field_type: &Type) -> Method1036     pub fn action(&self, field_type: &Type) -> Method {
1037         self.action
1038             .clone()
1039             .map(|p| p.resolve(field_type))
1040             .unwrap_or_else(|| {
1041                 if let Some(value_parser) = self.value_parser.as_ref() {
1042                     let span = value_parser.span();
1043                     default_action(field_type, span)
1044                 } else {
1045                     let span = self
1046                         .value_parser
1047                         .as_ref()
1048                         .map(|a| a.span())
1049                         .unwrap_or_else(|| self.kind.span());
1050                     default_action(field_type, span)
1051                 }
1052             })
1053     }
1054 
kind(&self) -> Sp<Kind>1055     pub fn kind(&self) -> Sp<Kind> {
1056         self.kind.clone()
1057     }
1058 
is_positional(&self) -> bool1059     pub fn is_positional(&self) -> bool {
1060         self.is_positional
1061     }
1062 
casing(&self) -> Sp<CasingStyle>1063     pub fn casing(&self) -> Sp<CasingStyle> {
1064         self.casing
1065     }
1066 
env_casing(&self) -> Sp<CasingStyle>1067     pub fn env_casing(&self) -> Sp<CasingStyle> {
1068         self.env_casing
1069     }
1070 
has_explicit_methods(&self) -> bool1071     pub fn has_explicit_methods(&self) -> bool {
1072         self.methods
1073             .iter()
1074             .any(|m| m.name != "help" && m.name != "long_help")
1075     }
1076 
skip_group(&self) -> bool1077     pub fn skip_group(&self) -> bool {
1078         self.skip_group
1079     }
1080 }
1081 
1082 #[derive(Clone)]
1083 enum ValueParser {
1084     Explicit(Method),
1085     Implicit(Ident),
1086 }
1087 
1088 impl ValueParser {
resolve(self, _inner_type: &Type) -> Method1089     fn resolve(self, _inner_type: &Type) -> Method {
1090         match self {
1091             Self::Explicit(method) => method,
1092             Self::Implicit(ident) => default_value_parser(_inner_type, ident.span()),
1093         }
1094     }
1095 
span(&self) -> Span1096     fn span(&self) -> Span {
1097         match self {
1098             Self::Explicit(method) => method.name.span(),
1099             Self::Implicit(ident) => ident.span(),
1100         }
1101     }
1102 }
1103 
default_value_parser(inner_type: &Type, span: Span) -> Method1104 fn default_value_parser(inner_type: &Type, span: Span) -> Method {
1105     let func = Ident::new("value_parser", span);
1106     Method::new(
1107         func,
1108         quote_spanned! { span=>
1109             clap::value_parser!(#inner_type)
1110         },
1111     )
1112 }
1113 
1114 #[derive(Clone)]
1115 pub enum Action {
1116     Explicit(Method),
1117     Implicit(Ident),
1118 }
1119 
1120 impl Action {
resolve(self, _field_type: &Type) -> Method1121     pub fn resolve(self, _field_type: &Type) -> Method {
1122         match self {
1123             Self::Explicit(method) => method,
1124             Self::Implicit(ident) => default_action(_field_type, ident.span()),
1125         }
1126     }
1127 
span(&self) -> Span1128     pub fn span(&self) -> Span {
1129         match self {
1130             Self::Explicit(method) => method.name.span(),
1131             Self::Implicit(ident) => ident.span(),
1132         }
1133     }
1134 }
1135 
default_action(field_type: &Type, span: Span) -> Method1136 fn default_action(field_type: &Type, span: Span) -> Method {
1137     let ty = Ty::from_syn_ty(field_type);
1138     let args = match *ty {
1139         Ty::Vec | Ty::OptionVec | Ty::VecVec | Ty::OptionVecVec => {
1140             quote_spanned! { span=>
1141                 clap::ArgAction::Append
1142             }
1143         }
1144         Ty::Option | Ty::OptionOption => {
1145             quote_spanned! { span=>
1146                 clap::ArgAction::Set
1147             }
1148         }
1149         _ => {
1150             if is_simple_ty(field_type, "bool") {
1151                 quote_spanned! { span=>
1152                     clap::ArgAction::SetTrue
1153                 }
1154             } else {
1155                 quote_spanned! { span=>
1156                     clap::ArgAction::Set
1157                 }
1158             }
1159         }
1160     };
1161 
1162     let func = Ident::new("action", span);
1163     Method::new(func, args)
1164 }
1165 
1166 #[allow(clippy::large_enum_variant)]
1167 #[derive(Clone)]
1168 pub enum Kind {
1169     Arg(Sp<Ty>),
1170     Command(Sp<Ty>),
1171     Value,
1172     FromGlobal(Sp<Ty>),
1173     Subcommand(Sp<Ty>),
1174     Flatten(Sp<Ty>),
1175     Skip(Option<AttrValue>, AttrKind),
1176     ExternalSubcommand,
1177 }
1178 
1179 impl Kind {
name(&self) -> &'static str1180     pub fn name(&self) -> &'static str {
1181         match self {
1182             Self::Arg(_) => "arg",
1183             Self::Command(_) => "command",
1184             Self::Value => "value",
1185             Self::FromGlobal(_) => "from_global",
1186             Self::Subcommand(_) => "subcommand",
1187             Self::Flatten(_) => "flatten",
1188             Self::Skip(_, _) => "skip",
1189             Self::ExternalSubcommand => "external_subcommand",
1190         }
1191     }
1192 
attr_kind(&self) -> AttrKind1193     pub fn attr_kind(&self) -> AttrKind {
1194         match self {
1195             Self::Arg(_) => AttrKind::Arg,
1196             Self::Command(_) => AttrKind::Command,
1197             Self::Value => AttrKind::Value,
1198             Self::FromGlobal(_) => AttrKind::Arg,
1199             Self::Subcommand(_) => AttrKind::Command,
1200             Self::Flatten(_) => AttrKind::Command,
1201             Self::Skip(_, kind) => *kind,
1202             Self::ExternalSubcommand => AttrKind::Command,
1203         }
1204     }
1205 
ty(&self) -> Option<&Sp<Ty>>1206     pub fn ty(&self) -> Option<&Sp<Ty>> {
1207         match self {
1208             Self::Arg(ty)
1209             | Self::Command(ty)
1210             | Self::Flatten(ty)
1211             | Self::FromGlobal(ty)
1212             | Self::Subcommand(ty) => Some(ty),
1213             Self::Value | Self::Skip(_, _) | Self::ExternalSubcommand => None,
1214         }
1215     }
1216 }
1217 
1218 #[derive(Clone)]
1219 pub struct Method {
1220     name: Ident,
1221     args: TokenStream,
1222 }
1223 
1224 impl Method {
new(name: Ident, args: TokenStream) -> Self1225     pub fn new(name: Ident, args: TokenStream) -> Self {
1226         Method { name, args }
1227     }
1228 
from_env(ident: Ident, env_var: &str) -> Result<Option<Self>, syn::Error>1229     fn from_env(ident: Ident, env_var: &str) -> Result<Option<Self>, syn::Error> {
1230         let mut lit = match env::var(env_var) {
1231             Ok(val) => {
1232                 if val.is_empty() {
1233                     return Ok(None);
1234                 }
1235                 LitStr::new(&val, ident.span())
1236             }
1237             Err(_) => {
1238                 abort!(
1239                     ident,
1240                     "cannot derive `{}` from Cargo.toml\n\n= note: {note}\n\n= help: {help}\n\n",
1241                     ident,
1242                     note = format_args!("`{env_var}` environment variable is not set"),
1243                     help = format_args!("use `{ident} = \"...\"` to set {ident} manually")
1244                 );
1245             }
1246         };
1247 
1248         if ident == "author" {
1249             let edited = process_author_str(&lit.value());
1250             lit = LitStr::new(&edited, lit.span());
1251         }
1252 
1253         Ok(Some(Method::new(ident, quote!(#lit))))
1254     }
1255 
args(&self) -> &TokenStream1256     pub(crate) fn args(&self) -> &TokenStream {
1257         &self.args
1258     }
1259 }
1260 
1261 impl ToTokens for Method {
to_tokens(&self, ts: &mut proc_macro2::TokenStream)1262     fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
1263         let Method { ref name, ref args } = self;
1264 
1265         let tokens = quote!( .#name(#args) );
1266 
1267         tokens.to_tokens(ts);
1268     }
1269 }
1270 
1271 #[derive(Clone)]
1272 pub struct Deprecation {
1273     pub span: Span,
1274     pub id: &'static str,
1275     pub version: &'static str,
1276     pub description: String,
1277 }
1278 
1279 impl Deprecation {
attribute(version: &'static str, old: AttrKind, new: AttrKind, span: Span) -> Self1280     fn attribute(version: &'static str, old: AttrKind, new: AttrKind, span: Span) -> Self {
1281         Self {
1282             span,
1283             id: "old_attribute",
1284             version,
1285             description: format!(
1286                 "Attribute `#[{}(...)]` has been deprecated in favor of `#[{}(...)]`",
1287                 old.as_str(),
1288                 new.as_str()
1289             ),
1290         }
1291     }
1292 }
1293 
1294 impl ToTokens for Deprecation {
to_tokens(&self, ts: &mut proc_macro2::TokenStream)1295     fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
1296         let tokens = if cfg!(feature = "deprecated") {
1297             let Deprecation {
1298                 span,
1299                 id,
1300                 version,
1301                 description,
1302             } = self;
1303             let span = *span;
1304             let id = Ident::new(id, span);
1305 
1306             quote_spanned!(span=> {
1307                 #[deprecated(since = #version, note = #description)]
1308                 fn #id() {}
1309                 #id();
1310             })
1311         } else {
1312             quote!()
1313         };
1314 
1315         tokens.to_tokens(ts);
1316     }
1317 }
1318 
assert_attr_kind(attr: &ClapAttr, possible_kind: &[AttrKind]) -> Result<(), syn::Error>1319 fn assert_attr_kind(attr: &ClapAttr, possible_kind: &[AttrKind]) -> Result<(), syn::Error> {
1320     if *attr.kind.get() == AttrKind::Clap || *attr.kind.get() == AttrKind::StructOpt {
1321         // deprecated
1322     } else if !possible_kind.contains(attr.kind.get()) {
1323         let options = possible_kind
1324             .iter()
1325             .map(|k| format!("`#[{}({})]`", k.as_str(), attr.name))
1326             .collect::<Vec<_>>();
1327         abort!(
1328             attr.name,
1329             "Unknown `#[{}({})]` attribute ({} exists)",
1330             attr.kind.as_str(),
1331             attr.name,
1332             options.join(", ")
1333         );
1334     }
1335     Ok(())
1336 }
1337 
1338 /// replace all `:` with `, ` when not inside the `<>`
1339 ///
1340 /// `"author1:author2:author3" => "author1, author2, author3"`
1341 /// `"author1 <http://website1.com>:author2" => "author1 <http://website1.com>, author2"
process_author_str(author: &str) -> String1342 fn process_author_str(author: &str) -> String {
1343     let mut res = String::with_capacity(author.len());
1344     let mut inside_angle_braces = 0usize;
1345 
1346     for ch in author.chars() {
1347         if inside_angle_braces > 0 && ch == '>' {
1348             inside_angle_braces -= 1;
1349             res.push(ch);
1350         } else if ch == '<' {
1351             inside_angle_braces += 1;
1352             res.push(ch);
1353         } else if inside_angle_braces == 0 && ch == ':' {
1354             res.push_str(", ");
1355         } else {
1356             res.push(ch);
1357         }
1358     }
1359 
1360     res
1361 }
1362 
1363 /// Defines the casing for the attributes long representation.
1364 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
1365 pub enum CasingStyle {
1366     /// Indicate word boundaries with uppercase letter, excluding the first word.
1367     Camel,
1368     /// Keep all letters lowercase and indicate word boundaries with hyphens.
1369     Kebab,
1370     /// Indicate word boundaries with uppercase letter, including the first word.
1371     Pascal,
1372     /// Keep all letters uppercase and indicate word boundaries with underscores.
1373     ScreamingSnake,
1374     /// Keep all letters lowercase and indicate word boundaries with underscores.
1375     Snake,
1376     /// Keep all letters lowercase and remove word boundaries.
1377     Lower,
1378     /// Keep all letters uppercase and remove word boundaries.
1379     Upper,
1380     /// Use the original attribute name defined in the code.
1381     Verbatim,
1382 }
1383 
1384 impl CasingStyle {
from_lit(name: &LitStr) -> Result<Sp<Self>, syn::Error>1385     fn from_lit(name: &LitStr) -> Result<Sp<Self>, syn::Error> {
1386         use self::CasingStyle::*;
1387 
1388         let normalized = name.value().to_upper_camel_case().to_lowercase();
1389         let cs = |kind| Sp::new(kind, name.span());
1390 
1391         let s = match normalized.as_ref() {
1392             "camel" | "camelcase" => cs(Camel),
1393             "kebab" | "kebabcase" => cs(Kebab),
1394             "pascal" | "pascalcase" => cs(Pascal),
1395             "screamingsnake" | "screamingsnakecase" => cs(ScreamingSnake),
1396             "snake" | "snakecase" => cs(Snake),
1397             "lower" | "lowercase" => cs(Lower),
1398             "upper" | "uppercase" => cs(Upper),
1399             "verbatim" | "verbatimcase" => cs(Verbatim),
1400             s => abort!(name, "unsupported casing: `{s}`"),
1401         };
1402         Ok(s)
1403     }
1404 }
1405 
1406 #[derive(Clone)]
1407 pub enum Name {
1408     Derived(Ident),
1409     Assigned(TokenStream),
1410 }
1411 
1412 impl Name {
raw(self) -> TokenStream1413     pub fn raw(self) -> TokenStream {
1414         match self {
1415             Name::Assigned(tokens) => tokens,
1416             Name::Derived(ident) => {
1417                 let s = ident.unraw().to_string();
1418                 quote_spanned!(ident.span()=> #s)
1419             }
1420         }
1421     }
1422 
translate(self, style: CasingStyle) -> TokenStream1423     pub fn translate(self, style: CasingStyle) -> TokenStream {
1424         use CasingStyle::*;
1425 
1426         match self {
1427             Name::Assigned(tokens) => tokens,
1428             Name::Derived(ident) => {
1429                 let s = ident.unraw().to_string();
1430                 let s = match style {
1431                     Pascal => s.to_upper_camel_case(),
1432                     Kebab => s.to_kebab_case(),
1433                     Camel => s.to_lower_camel_case(),
1434                     ScreamingSnake => s.to_shouty_snake_case(),
1435                     Snake => s.to_snake_case(),
1436                     Lower => s.to_snake_case().replace('_', ""),
1437                     Upper => s.to_shouty_snake_case().replace('_', ""),
1438                     Verbatim => s,
1439                 };
1440                 quote_spanned!(ident.span()=> #s)
1441             }
1442         }
1443     }
1444 
translate_char(self, style: CasingStyle) -> TokenStream1445     pub fn translate_char(self, style: CasingStyle) -> TokenStream {
1446         use CasingStyle::*;
1447 
1448         match self {
1449             Name::Assigned(tokens) => quote!( (#tokens).chars().next().unwrap() ),
1450             Name::Derived(ident) => {
1451                 let s = ident.unraw().to_string();
1452                 let s = match style {
1453                     Pascal => s.to_upper_camel_case(),
1454                     Kebab => s.to_kebab_case(),
1455                     Camel => s.to_lower_camel_case(),
1456                     ScreamingSnake => s.to_shouty_snake_case(),
1457                     Snake => s.to_snake_case(),
1458                     Lower => s.to_snake_case(),
1459                     Upper => s.to_shouty_snake_case(),
1460                     Verbatim => s,
1461                 };
1462 
1463                 let s = s.chars().next().unwrap();
1464                 quote_spanned!(ident.span()=> #s)
1465             }
1466         }
1467     }
1468 }
1469