clap_derive/
item.rs

1// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
2// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
3// Ana Hobden (@hoverbear) <operator@hoverbear.org>
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
15use std::env;
16
17use heck::{ToKebabCase, ToLowerCamelCase, ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};
18use proc_macro2::{self, Span, TokenStream};
19use quote::{format_ident, quote, quote_spanned, ToTokens};
20use syn::DeriveInput;
21use syn::{self, ext::IdentExt, spanned::Spanned, Attribute, Field, Ident, LitStr, Type, Variant};
22
23use crate::attr::{AttrKind, AttrValue, ClapAttr, MagicAttrName};
24use crate::utils::{extract_doc_comment, format_doc_comment, inner_type, is_simple_ty, Sp, Ty};
25
26/// Default casing style for generated arguments.
27pub(crate) const DEFAULT_CASING: CasingStyle = CasingStyle::Kebab;
28
29/// Default casing style for environment variables
30pub(crate) const DEFAULT_ENV_CASING: CasingStyle = CasingStyle::ScreamingSnake;
31
32#[derive(Clone)]
33pub(crate) 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
55impl Item {
56    pub(crate) 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
73    pub(crate) fn from_subcommand_enum(
74        input: &DeriveInput,
75        name: Name,
76    ) -> Result<Self, syn::Error> {
77        let ident = input.ident.clone();
78        let span = input.ident.span();
79        let attrs = &input.attrs;
80        let argument_casing = Sp::new(DEFAULT_CASING, span);
81        let env_casing = Sp::new(DEFAULT_ENV_CASING, span);
82        let kind = Sp::new(Kind::Command(Sp::new(Ty::Other, span)), span);
83
84        let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind);
85        let parsed_attrs = ClapAttr::parse_all(attrs)?;
86        res.infer_kind(&parsed_attrs)?;
87        res.push_attrs(&parsed_attrs)?;
88        res.push_doc_comment(attrs, "about", Some("long_about"));
89
90        Ok(res)
91    }
92
93    pub(crate) fn from_value_enum(input: &DeriveInput, name: Name) -> Result<Self, syn::Error> {
94        let ident = input.ident.clone();
95        let span = input.ident.span();
96        let attrs = &input.attrs;
97        let argument_casing = Sp::new(DEFAULT_CASING, span);
98        let env_casing = Sp::new(DEFAULT_ENV_CASING, span);
99        let kind = Sp::new(Kind::Value, span);
100
101        let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind);
102        let parsed_attrs = ClapAttr::parse_all(attrs)?;
103        res.infer_kind(&parsed_attrs)?;
104        res.push_attrs(&parsed_attrs)?;
105        // Ignoring `push_doc_comment` as there is no top-level clap builder to add documentation
106        // to
107
108        if res.has_explicit_methods() {
109            abort!(
110                res.methods[0].name.span(),
111                "{} doesn't exist for `ValueEnum` enums",
112                res.methods[0].name
113            );
114        }
115
116        Ok(res)
117    }
118
119    pub(crate) fn from_subcommand_variant(
120        variant: &Variant,
121        struct_casing: Sp<CasingStyle>,
122        env_casing: Sp<CasingStyle>,
123    ) -> Result<Self, syn::Error> {
124        let name = variant.ident.clone();
125        let ident = variant.ident.clone();
126        let span = variant.span();
127        let ty = match variant.fields {
128            syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
129                Ty::from_syn_ty(&unnamed[0].ty)
130            }
131            syn::Fields::Named(_) | syn::Fields::Unnamed(..) | syn::Fields::Unit => {
132                Sp::new(Ty::Other, span)
133            }
134        };
135        let kind = Sp::new(Kind::Command(ty), span);
136        let mut res = Self::new(
137            Name::Derived(name),
138            ident,
139            None,
140            struct_casing,
141            env_casing,
142            kind,
143        );
144        let parsed_attrs = ClapAttr::parse_all(&variant.attrs)?;
145        res.infer_kind(&parsed_attrs)?;
146        res.push_attrs(&parsed_attrs)?;
147        if matches!(&*res.kind, Kind::Command(_) | Kind::Subcommand(_)) {
148            res.push_doc_comment(&variant.attrs, "about", Some("long_about"));
149        }
150
151        match &*res.kind {
152            Kind::Flatten(_) => {
153                if res.has_explicit_methods() {
154                    abort!(
155                        res.kind.span(),
156                        "methods are not allowed for flattened entry"
157                    );
158                }
159            }
160
161            Kind::Subcommand(_)
162            | Kind::ExternalSubcommand
163            | Kind::FromGlobal(_)
164            | Kind::Skip(_, _)
165            | Kind::Command(_)
166            | Kind::Value
167            | Kind::Arg(_) => (),
168        }
169
170        Ok(res)
171    }
172
173    pub(crate) fn from_value_enum_variant(
174        variant: &Variant,
175        argument_casing: Sp<CasingStyle>,
176        env_casing: Sp<CasingStyle>,
177    ) -> Result<Self, syn::Error> {
178        let ident = variant.ident.clone();
179        let span = variant.span();
180        let kind = Sp::new(Kind::Value, span);
181        let mut res = Self::new(
182            Name::Derived(variant.ident.clone()),
183            ident,
184            None,
185            argument_casing,
186            env_casing,
187            kind,
188        );
189        let parsed_attrs = ClapAttr::parse_all(&variant.attrs)?;
190        res.infer_kind(&parsed_attrs)?;
191        res.push_attrs(&parsed_attrs)?;
192        if matches!(&*res.kind, Kind::Value) {
193            res.push_doc_comment(&variant.attrs, "help", None);
194        }
195
196        Ok(res)
197    }
198
199    pub(crate) fn from_args_field(
200        field: &Field,
201        struct_casing: Sp<CasingStyle>,
202        env_casing: Sp<CasingStyle>,
203    ) -> Result<Self, syn::Error> {
204        let name = field.ident.clone().unwrap();
205        let ident = field.ident.clone().unwrap();
206        let span = field.span();
207        let ty = Ty::from_syn_ty(&field.ty);
208        let kind = Sp::new(Kind::Arg(ty), span);
209        let mut res = Self::new(
210            Name::Derived(name),
211            ident,
212            Some(field.ty.clone()),
213            struct_casing,
214            env_casing,
215            kind,
216        );
217        let parsed_attrs = ClapAttr::parse_all(&field.attrs)?;
218        res.infer_kind(&parsed_attrs)?;
219        res.push_attrs(&parsed_attrs)?;
220        if matches!(&*res.kind, Kind::Arg(_)) {
221            res.push_doc_comment(&field.attrs, "help", Some("long_help"));
222        }
223
224        match &*res.kind {
225            Kind::Flatten(_) => {
226                if res.has_explicit_methods() {
227                    abort!(
228                        res.kind.span(),
229                        "methods are not allowed for flattened entry"
230                    );
231                }
232            }
233
234            Kind::Subcommand(_) => {
235                if res.has_explicit_methods() {
236                    abort!(
237                        res.kind.span(),
238                        "methods in attributes are not allowed for subcommand"
239                    );
240                }
241            }
242            Kind::Skip(_, _)
243            | Kind::FromGlobal(_)
244            | Kind::Arg(_)
245            | Kind::Command(_)
246            | Kind::Value
247            | Kind::ExternalSubcommand => {}
248        }
249
250        Ok(res)
251    }
252
253    fn new(
254        name: Name,
255        ident: Ident,
256        ty: Option<Type>,
257        casing: Sp<CasingStyle>,
258        env_casing: Sp<CasingStyle>,
259        kind: Sp<Kind>,
260    ) -> Self {
261        let group_id = Name::Derived(ident);
262        Self {
263            name,
264            ty,
265            casing,
266            env_casing,
267            doc_comment: vec![],
268            methods: vec![],
269            deprecations: vec![],
270            value_parser: None,
271            action: None,
272            verbatim_doc_comment: false,
273            force_long_help: false,
274            next_display_order: None,
275            next_help_heading: None,
276            is_enum: false,
277            is_positional: true,
278            skip_group: false,
279            group_id,
280            group_methods: vec![],
281            kind,
282        }
283    }
284
285    fn push_method(&mut self, kind: AttrKind, name: Ident, arg: impl ToTokens) {
286        self.push_method_(kind, name, arg.to_token_stream());
287    }
288
289    fn push_method_(&mut self, kind: AttrKind, name: Ident, arg: TokenStream) {
290        if name == "id" {
291            match kind {
292                AttrKind::Command | AttrKind::Value => {
293                    self.deprecations.push(Deprecation {
294                        span: name.span(),
295                        id: "id_is_only_for_arg",
296                        version: "4.0.0",
297                        description: format!(
298                            "`#[{}(id)] was allowed by mistake, instead use `#[{}(name)]`",
299                            kind.as_str(),
300                            kind.as_str()
301                        ),
302                    });
303                    self.name = Name::Assigned(arg);
304                }
305                AttrKind::Group => {
306                    self.group_id = Name::Assigned(arg);
307                }
308                AttrKind::Arg | AttrKind::Clap | AttrKind::StructOpt => {
309                    self.name = Name::Assigned(arg);
310                }
311            }
312        } else if name == "name" {
313            match kind {
314                AttrKind::Arg => {
315                    self.deprecations.push(Deprecation {
316                        span: name.span(),
317                        id: "id_is_only_for_arg",
318                        version: "4.0.0",
319                        description: format!(
320                            "`#[{}(name)] was allowed by mistake, instead use `#[{}(id)]` or `#[{}(value_name)]`",
321                            kind.as_str(),
322                            kind.as_str(),
323                            kind.as_str()
324                        ),
325                    });
326                    self.name = Name::Assigned(arg);
327                }
328                AttrKind::Group => self.group_methods.push(Method::new(name, arg)),
329                AttrKind::Command | AttrKind::Value | AttrKind::Clap | AttrKind::StructOpt => {
330                    self.name = Name::Assigned(arg);
331                }
332            }
333        } else if name == "value_parser" {
334            self.value_parser = Some(ValueParser::Explicit(Method::new(name, arg)));
335        } else if name == "action" {
336            self.action = Some(Action::Explicit(Method::new(name, arg)));
337        } else {
338            if name == "short" || name == "long" {
339                self.is_positional = false;
340            }
341            match kind {
342                AttrKind::Group => self.group_methods.push(Method::new(name, arg)),
343                _ => self.methods.push(Method::new(name, arg)),
344            };
345        }
346    }
347
348    fn infer_kind(&mut self, attrs: &[ClapAttr]) -> Result<(), syn::Error> {
349        for attr in attrs {
350            if let Some(AttrValue::Call(_)) = &attr.value {
351                continue;
352            }
353
354            let actual_attr_kind = *attr.kind.get();
355            let kind = match &attr.magic {
356                Some(MagicAttrName::FromGlobal) => {
357                    if attr.value.is_some() {
358                        let expr = attr.value_or_abort()?;
359                        abort!(expr, "attribute `{}` does not accept a value", attr.name);
360                    }
361                    let ty = self
362                        .kind()
363                        .ty()
364                        .cloned()
365                        .unwrap_or_else(|| Sp::new(Ty::Other, self.kind.span()));
366                    let kind = Sp::new(Kind::FromGlobal(ty), attr.name.span());
367                    Some(kind)
368                }
369                Some(MagicAttrName::Subcommand) if attr.value.is_none() => {
370                    if attr.value.is_some() {
371                        let expr = attr.value_or_abort()?;
372                        abort!(expr, "attribute `{}` does not accept a value", attr.name);
373                    }
374                    let ty = self
375                        .kind()
376                        .ty()
377                        .cloned()
378                        .unwrap_or_else(|| Sp::new(Ty::Other, self.kind.span()));
379                    let kind = Sp::new(Kind::Subcommand(ty), attr.name.span());
380                    Some(kind)
381                }
382                Some(MagicAttrName::ExternalSubcommand) if attr.value.is_none() => {
383                    if attr.value.is_some() {
384                        let expr = attr.value_or_abort()?;
385                        abort!(expr, "attribute `{}` does not accept a value", attr.name);
386                    }
387                    let kind = Sp::new(Kind::ExternalSubcommand, attr.name.span());
388                    Some(kind)
389                }
390                Some(MagicAttrName::Flatten) if attr.value.is_none() => {
391                    if attr.value.is_some() {
392                        let expr = attr.value_or_abort()?;
393                        abort!(expr, "attribute `{}` does not accept a value", attr.name);
394                    }
395                    let ty = self
396                        .kind()
397                        .ty()
398                        .cloned()
399                        .unwrap_or_else(|| Sp::new(Ty::Other, self.kind.span()));
400                    let kind = Sp::new(Kind::Flatten(ty), attr.name.span());
401                    Some(kind)
402                }
403                Some(MagicAttrName::Skip) if actual_attr_kind != AttrKind::Group => {
404                    let expr = attr.value.clone();
405                    let kind = Sp::new(Kind::Skip(expr, self.kind.attr_kind()), attr.name.span());
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
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://docs.rs/clap/latest/clap/_derive/index.html#arg-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.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.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.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://docs.rs/clap/latest/clap/_derive/index.html#arg-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://docs.rs/clap/latest/clap/_derive/index.html#arg-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.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.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.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://docs.rs/clap/latest/clap/_derive/index.html#arg-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.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.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.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://docs.rs/clap/latest/clap/_derive/index.html#arg-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://docs.rs/clap/latest/clap/_derive/index.html#arg-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.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.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.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
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
905            let is_value_kind = matches!(self.kind.get(), Kind::Value);
906            let short_method = if is_value_kind && cfg!(feature = "unstable-v5") {
907                Method::new(
908                    short_name,
909                    long_help
910                        .clone()
911                        .or(short_help)
912                        .map(|h| quote!(#h))
913                        .unwrap_or_else(|| quote!(None)),
914                )
915            } else {
916                Method::new(
917                    short_name,
918                    short_help
919                        .map(|h| quote!(#h))
920                        .unwrap_or_else(|| quote!(None)),
921                )
922            };
923            self.doc_comment.push(short_method);
924            if let Some(long_name) = long_name {
925                let long_name = format_ident!("{long_name}");
926                let long = Method::new(
927                    long_name,
928                    long_help
929                        .map(|h| quote!(#h))
930                        .unwrap_or_else(|| quote!(None)),
931                );
932                self.doc_comment.push(long);
933            }
934        }
935    }
936
937    fn set_kind(&mut self, kind: Sp<Kind>) -> Result<(), syn::Error> {
938        match (self.kind.get(), kind.get()) {
939            (Kind::Arg(_), Kind::FromGlobal(_))
940            | (Kind::Arg(_), Kind::Subcommand(_))
941            | (Kind::Arg(_), Kind::Flatten(_))
942            | (Kind::Arg(_), Kind::Skip(_, _))
943            | (Kind::Command(_), Kind::Subcommand(_))
944            | (Kind::Command(_), Kind::Flatten(_))
945            | (Kind::Command(_), Kind::Skip(_, _))
946            | (Kind::Command(_), Kind::ExternalSubcommand)
947            | (Kind::Value, Kind::Skip(_, _)) => {
948                self.kind = kind;
949            }
950
951            (_, _) => {
952                let old = self.kind.name();
953                let new = kind.name();
954                abort!(kind.span(), "`{new}` cannot be used with `{old}`");
955            }
956        }
957        Ok(())
958    }
959
960    pub(crate) fn find_default_method(&self) -> Option<&Method> {
961        self.methods
962            .iter()
963            .find(|m| m.name == "default_value" || m.name == "default_value_os")
964    }
965
966    /// generate methods from attributes on top of struct or enum
967    pub(crate) fn initial_top_level_methods(&self) -> TokenStream {
968        let next_display_order = self.next_display_order.as_ref().into_iter();
969        let next_help_heading = self.next_help_heading.as_ref().into_iter();
970        quote!(
971            #(#next_display_order)*
972            #(#next_help_heading)*
973        )
974    }
975
976    pub(crate) fn final_top_level_methods(&self) -> TokenStream {
977        let methods = &self.methods;
978        let doc_comment = &self.doc_comment;
979
980        quote!( #(#doc_comment)* #(#methods)*)
981    }
982
983    /// generate methods on top of a field
984    pub(crate) fn field_methods(&self) -> TokenStream {
985        let methods = &self.methods;
986        let doc_comment = &self.doc_comment;
987        quote!( #(#doc_comment)* #(#methods)* )
988    }
989
990    pub(crate) fn group_id(&self) -> &Name {
991        &self.group_id
992    }
993
994    pub(crate) fn group_methods(&self) -> TokenStream {
995        let group_methods = &self.group_methods;
996        quote!( #(#group_methods)* )
997    }
998
999    pub(crate) fn deprecations(&self) -> TokenStream {
1000        let deprecations = &self.deprecations;
1001        quote!( #(#deprecations)* )
1002    }
1003
1004    pub(crate) fn next_display_order(&self) -> TokenStream {
1005        let next_display_order = self.next_display_order.as_ref().into_iter();
1006        quote!( #(#next_display_order)* )
1007    }
1008
1009    pub(crate) fn next_help_heading(&self) -> TokenStream {
1010        let next_help_heading = self.next_help_heading.as_ref().into_iter();
1011        quote!( #(#next_help_heading)* )
1012    }
1013
1014    pub(crate) fn id(&self) -> &Name {
1015        &self.name
1016    }
1017
1018    pub(crate) fn cased_name(&self) -> TokenStream {
1019        self.name.clone().translate(*self.casing)
1020    }
1021
1022    pub(crate) fn value_name(&self) -> TokenStream {
1023        self.name.clone().translate(CasingStyle::ScreamingSnake)
1024    }
1025
1026    pub(crate) fn value_parser(&self, field_type: &Type) -> Method {
1027        self.value_parser
1028            .clone()
1029            .map(|p| {
1030                let inner_type = inner_type(field_type);
1031                p.resolve(inner_type)
1032            })
1033            .unwrap_or_else(|| {
1034                let inner_type = inner_type(field_type);
1035                if let Some(action) = self.action.as_ref() {
1036                    let span = action.span();
1037                    default_value_parser(inner_type, span)
1038                } else {
1039                    let span = self
1040                        .action
1041                        .as_ref()
1042                        .map(|a| a.span())
1043                        .unwrap_or_else(|| self.kind.span());
1044                    default_value_parser(inner_type, span)
1045                }
1046            })
1047    }
1048
1049    pub(crate) fn action(&self, field_type: &Type) -> Method {
1050        self.action
1051            .clone()
1052            .map(|p| p.resolve(field_type))
1053            .unwrap_or_else(|| {
1054                if let Some(value_parser) = self.value_parser.as_ref() {
1055                    let span = value_parser.span();
1056                    default_action(field_type, span)
1057                } else {
1058                    let span = self
1059                        .value_parser
1060                        .as_ref()
1061                        .map(|a| a.span())
1062                        .unwrap_or_else(|| self.kind.span());
1063                    default_action(field_type, span)
1064                }
1065            })
1066    }
1067
1068    pub(crate) fn kind(&self) -> Sp<Kind> {
1069        self.kind.clone()
1070    }
1071
1072    pub(crate) fn is_positional(&self) -> bool {
1073        self.is_positional
1074    }
1075
1076    pub(crate) fn casing(&self) -> Sp<CasingStyle> {
1077        self.casing
1078    }
1079
1080    pub(crate) fn env_casing(&self) -> Sp<CasingStyle> {
1081        self.env_casing
1082    }
1083
1084    pub(crate) fn has_explicit_methods(&self) -> bool {
1085        self.methods
1086            .iter()
1087            .any(|m| m.name != "help" && m.name != "long_help")
1088    }
1089
1090    pub(crate) fn skip_group(&self) -> bool {
1091        self.skip_group
1092    }
1093}
1094
1095#[derive(Clone)]
1096enum ValueParser {
1097    Explicit(Method),
1098    Implicit(Ident),
1099}
1100
1101impl ValueParser {
1102    fn resolve(self, _inner_type: &Type) -> Method {
1103        match self {
1104            Self::Explicit(method) => method,
1105            Self::Implicit(ident) => default_value_parser(_inner_type, ident.span()),
1106        }
1107    }
1108
1109    fn span(&self) -> Span {
1110        match self {
1111            Self::Explicit(method) => method.name.span(),
1112            Self::Implicit(ident) => ident.span(),
1113        }
1114    }
1115}
1116
1117fn default_value_parser(inner_type: &Type, span: Span) -> Method {
1118    let func = Ident::new("value_parser", span);
1119    Method::new(
1120        func,
1121        quote_spanned! { span=>
1122            clap::value_parser!(#inner_type)
1123        },
1124    )
1125}
1126
1127#[derive(Clone)]
1128pub(crate) enum Action {
1129    Explicit(Method),
1130    Implicit(Ident),
1131}
1132
1133impl Action {
1134    pub(crate) fn resolve(self, _field_type: &Type) -> Method {
1135        match self {
1136            Self::Explicit(method) => method,
1137            Self::Implicit(ident) => default_action(_field_type, ident.span()),
1138        }
1139    }
1140
1141    pub(crate) fn span(&self) -> Span {
1142        match self {
1143            Self::Explicit(method) => method.name.span(),
1144            Self::Implicit(ident) => ident.span(),
1145        }
1146    }
1147}
1148
1149fn default_action(field_type: &Type, span: Span) -> Method {
1150    let ty = Ty::from_syn_ty(field_type);
1151    let args = match *ty {
1152        Ty::Vec | Ty::OptionVec | Ty::VecVec | Ty::OptionVecVec => {
1153            quote_spanned! { span=>
1154                clap::ArgAction::Append
1155            }
1156        }
1157        Ty::Option | Ty::OptionOption => {
1158            quote_spanned! { span=>
1159                clap::ArgAction::Set
1160            }
1161        }
1162        _ => {
1163            if is_simple_ty(field_type, "bool") {
1164                quote_spanned! { span=>
1165                    clap::ArgAction::SetTrue
1166                }
1167            } else {
1168                quote_spanned! { span=>
1169                    clap::ArgAction::Set
1170                }
1171            }
1172        }
1173    };
1174
1175    let func = Ident::new("action", span);
1176    Method::new(func, args)
1177}
1178
1179#[allow(clippy::large_enum_variant)]
1180#[derive(Clone)]
1181pub(crate) enum Kind {
1182    Arg(Sp<Ty>),
1183    Command(Sp<Ty>),
1184    Value,
1185    FromGlobal(Sp<Ty>),
1186    Subcommand(Sp<Ty>),
1187    Flatten(Sp<Ty>),
1188    Skip(Option<AttrValue>, AttrKind),
1189    ExternalSubcommand,
1190}
1191
1192impl Kind {
1193    pub(crate) fn name(&self) -> &'static str {
1194        match self {
1195            Self::Arg(_) => "arg",
1196            Self::Command(_) => "command",
1197            Self::Value => "value",
1198            Self::FromGlobal(_) => "from_global",
1199            Self::Subcommand(_) => "subcommand",
1200            Self::Flatten(_) => "flatten",
1201            Self::Skip(_, _) => "skip",
1202            Self::ExternalSubcommand => "external_subcommand",
1203        }
1204    }
1205
1206    pub(crate) fn attr_kind(&self) -> AttrKind {
1207        match self {
1208            Self::Arg(_) => AttrKind::Arg,
1209            Self::Command(_) => AttrKind::Command,
1210            Self::Value => AttrKind::Value,
1211            Self::FromGlobal(_) => AttrKind::Arg,
1212            Self::Subcommand(_) => AttrKind::Command,
1213            Self::Flatten(_) => AttrKind::Command,
1214            Self::Skip(_, kind) => *kind,
1215            Self::ExternalSubcommand => AttrKind::Command,
1216        }
1217    }
1218
1219    pub(crate) fn ty(&self) -> Option<&Sp<Ty>> {
1220        match self {
1221            Self::Arg(ty)
1222            | Self::Command(ty)
1223            | Self::Flatten(ty)
1224            | Self::FromGlobal(ty)
1225            | Self::Subcommand(ty) => Some(ty),
1226            Self::Value | Self::Skip(_, _) | Self::ExternalSubcommand => None,
1227        }
1228    }
1229}
1230
1231#[derive(Clone)]
1232pub(crate) struct Method {
1233    name: Ident,
1234    args: TokenStream,
1235}
1236
1237impl Method {
1238    pub(crate) fn new(name: Ident, args: TokenStream) -> Self {
1239        Method { name, args }
1240    }
1241
1242    fn from_env(ident: Ident, env_var: &str) -> Result<Option<Self>, syn::Error> {
1243        let mut lit = match env::var(env_var) {
1244            Ok(val) => {
1245                if val.is_empty() {
1246                    return Ok(None);
1247                }
1248                LitStr::new(&val, ident.span())
1249            }
1250            Err(_) => {
1251                abort!(
1252                    ident,
1253                    "cannot derive `{}` from Cargo.toml\n\n= note: {note}\n\n= help: {help}\n\n",
1254                    ident,
1255                    note = format_args!("`{env_var}` environment variable is not set"),
1256                    help = format_args!("use `{ident} = \"...\"` to set {ident} manually")
1257                );
1258            }
1259        };
1260
1261        if ident == "author" {
1262            let edited = process_author_str(&lit.value());
1263            lit = LitStr::new(&edited, lit.span());
1264        }
1265
1266        Ok(Some(Method::new(ident, quote!(#lit))))
1267    }
1268
1269    pub(crate) fn args(&self) -> &TokenStream {
1270        &self.args
1271    }
1272}
1273
1274impl ToTokens for Method {
1275    fn to_tokens(&self, ts: &mut TokenStream) {
1276        let Method { ref name, ref args } = self;
1277
1278        let tokens = quote!( .#name(#args) );
1279
1280        tokens.to_tokens(ts);
1281    }
1282}
1283
1284#[derive(Clone)]
1285pub(crate) struct Deprecation {
1286    pub(crate) span: Span,
1287    pub(crate) id: &'static str,
1288    pub(crate) version: &'static str,
1289    pub(crate) description: String,
1290}
1291
1292impl Deprecation {
1293    fn attribute(version: &'static str, old: AttrKind, new: AttrKind, span: Span) -> Self {
1294        Self {
1295            span,
1296            id: "old_attribute",
1297            version,
1298            description: format!(
1299                "Attribute `#[{}(...)]` has been deprecated in favor of `#[{}(...)]`",
1300                old.as_str(),
1301                new.as_str()
1302            ),
1303        }
1304    }
1305}
1306
1307impl ToTokens for Deprecation {
1308    fn to_tokens(&self, ts: &mut TokenStream) {
1309        let tokens = if cfg!(feature = "deprecated") {
1310            let Deprecation {
1311                span,
1312                id,
1313                version,
1314                description,
1315            } = self;
1316            let span = *span;
1317            let id = Ident::new(id, span);
1318
1319            quote_spanned!(span=> {
1320                #[deprecated(since = #version, note = #description)]
1321                fn #id() {}
1322                #id();
1323            })
1324        } else {
1325            quote!()
1326        };
1327
1328        tokens.to_tokens(ts);
1329    }
1330}
1331
1332fn assert_attr_kind(attr: &ClapAttr, possible_kind: &[AttrKind]) -> Result<(), syn::Error> {
1333    if *attr.kind.get() == AttrKind::Clap || *attr.kind.get() == AttrKind::StructOpt {
1334        // deprecated
1335    } else if !possible_kind.contains(attr.kind.get()) {
1336        let options = possible_kind
1337            .iter()
1338            .map(|k| format!("`#[{}({})]`", k.as_str(), attr.name))
1339            .collect::<Vec<_>>();
1340        abort!(
1341            attr.name,
1342            "Unknown `#[{}({})]` attribute ({} exists)",
1343            attr.kind.as_str(),
1344            attr.name,
1345            options.join(", ")
1346        );
1347    }
1348    Ok(())
1349}
1350
1351/// replace all `:` with `, ` when not inside the `<>`
1352///
1353/// `"author1:author2:author3" => "author1, author2, author3"`
1354/// `"author1 <http://website1.com>:author2" => "author1 <http://website1.com>, author2"`
1355fn process_author_str(author: &str) -> String {
1356    let mut res = String::with_capacity(author.len());
1357    let mut inside_angle_braces = 0usize;
1358
1359    for ch in author.chars() {
1360        if inside_angle_braces > 0 && ch == '>' {
1361            inside_angle_braces -= 1;
1362            res.push(ch);
1363        } else if ch == '<' {
1364            inside_angle_braces += 1;
1365            res.push(ch);
1366        } else if inside_angle_braces == 0 && ch == ':' {
1367            res.push_str(", ");
1368        } else {
1369            res.push(ch);
1370        }
1371    }
1372
1373    res
1374}
1375
1376/// Defines the casing for the attributes long representation.
1377#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1378pub(crate) enum CasingStyle {
1379    /// Indicate word boundaries with uppercase letter, excluding the first word.
1380    Camel,
1381    /// Keep all letters lowercase and indicate word boundaries with hyphens.
1382    Kebab,
1383    /// Indicate word boundaries with uppercase letter, including the first word.
1384    Pascal,
1385    /// Keep all letters uppercase and indicate word boundaries with underscores.
1386    ScreamingSnake,
1387    /// Keep all letters lowercase and indicate word boundaries with underscores.
1388    Snake,
1389    /// Keep all letters lowercase and remove word boundaries.
1390    Lower,
1391    /// Keep all letters uppercase and remove word boundaries.
1392    Upper,
1393    /// Use the original attribute name defined in the code.
1394    Verbatim,
1395}
1396
1397impl CasingStyle {
1398    fn from_lit(name: &LitStr) -> Result<Sp<Self>, syn::Error> {
1399        use self::CasingStyle::{
1400            Camel, Kebab, Lower, Pascal, ScreamingSnake, Snake, Upper, Verbatim,
1401        };
1402
1403        let normalized = name.value().to_upper_camel_case().to_lowercase();
1404        let cs = |kind| Sp::new(kind, name.span());
1405
1406        let s = match normalized.as_ref() {
1407            "camel" | "camelcase" => cs(Camel),
1408            "kebab" | "kebabcase" => cs(Kebab),
1409            "pascal" | "pascalcase" => cs(Pascal),
1410            "screamingsnake" | "screamingsnakecase" => cs(ScreamingSnake),
1411            "snake" | "snakecase" => cs(Snake),
1412            "lower" | "lowercase" => cs(Lower),
1413            "upper" | "uppercase" => cs(Upper),
1414            "verbatim" | "verbatimcase" => cs(Verbatim),
1415            s => abort!(name, "unsupported casing: `{s}`"),
1416        };
1417        Ok(s)
1418    }
1419}
1420
1421#[derive(Clone)]
1422pub(crate) enum Name {
1423    Derived(Ident),
1424    Assigned(TokenStream),
1425}
1426
1427impl Name {
1428    pub(crate) fn translate(self, style: CasingStyle) -> TokenStream {
1429        use CasingStyle::{Camel, Kebab, Lower, Pascal, ScreamingSnake, Snake, Upper, Verbatim};
1430
1431        match self {
1432            Name::Assigned(tokens) => tokens,
1433            Name::Derived(ident) => {
1434                let s = ident.unraw().to_string();
1435                let s = match style {
1436                    Pascal => s.to_upper_camel_case(),
1437                    Kebab => s.to_kebab_case(),
1438                    Camel => s.to_lower_camel_case(),
1439                    ScreamingSnake => s.to_shouty_snake_case(),
1440                    Snake => s.to_snake_case(),
1441                    Lower => s.to_snake_case().replace('_', ""),
1442                    Upper => s.to_shouty_snake_case().replace('_', ""),
1443                    Verbatim => s,
1444                };
1445                quote_spanned!(ident.span()=> #s)
1446            }
1447        }
1448    }
1449
1450    pub(crate) fn translate_char(self, style: CasingStyle) -> TokenStream {
1451        use CasingStyle::{Camel, Kebab, Lower, Pascal, ScreamingSnake, Snake, Upper, Verbatim};
1452
1453        match self {
1454            Name::Assigned(tokens) => quote!( (#tokens).chars().next().unwrap() ),
1455            Name::Derived(ident) => {
1456                let s = ident.unraw().to_string();
1457                let s = match style {
1458                    Pascal => s.to_upper_camel_case(),
1459                    Kebab => s.to_kebab_case(),
1460                    Camel => s.to_lower_camel_case(),
1461                    ScreamingSnake => s.to_shouty_snake_case(),
1462                    Snake => s.to_snake_case(),
1463                    Lower => s.to_snake_case(),
1464                    Upper => s.to_shouty_snake_case(),
1465                    Verbatim => s,
1466                };
1467
1468                let s = s.chars().next().unwrap();
1469                quote_spanned!(ident.span()=> #s)
1470            }
1471        }
1472    }
1473}
1474
1475impl ToTokens for Name {
1476    fn to_tokens(&self, tokens: &mut TokenStream) {
1477        match self {
1478            Name::Assigned(t) => t.to_tokens(tokens),
1479            Name::Derived(ident) => {
1480                let s = ident.unraw().to_string();
1481                quote_spanned!(ident.span()=> #s).to_tokens(tokens);
1482            }
1483        }
1484    }
1485}