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://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.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://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.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://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.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://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.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            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
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
947    pub(crate) 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
954    pub(crate) 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
963    pub(crate) 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
971    pub(crate) fn field_methods(&self) -> TokenStream {
972        let methods = &self.methods;
973        let doc_comment = &self.doc_comment;
974        quote!( #(#doc_comment)* #(#methods)* )
975    }
976
977    pub(crate) fn group_id(&self) -> &Name {
978        &self.group_id
979    }
980
981    pub(crate) fn group_methods(&self) -> TokenStream {
982        let group_methods = &self.group_methods;
983        quote!( #(#group_methods)* )
984    }
985
986    pub(crate) fn deprecations(&self) -> TokenStream {
987        let deprecations = &self.deprecations;
988        quote!( #(#deprecations)* )
989    }
990
991    pub(crate) 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
996    pub(crate) 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
1001    pub(crate) fn id(&self) -> &Name {
1002        &self.name
1003    }
1004
1005    pub(crate) fn cased_name(&self) -> TokenStream {
1006        self.name.clone().translate(*self.casing)
1007    }
1008
1009    pub(crate) fn value_name(&self) -> TokenStream {
1010        self.name.clone().translate(CasingStyle::ScreamingSnake)
1011    }
1012
1013    pub(crate) 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
1036    pub(crate) 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
1055    pub(crate) fn kind(&self) -> Sp<Kind> {
1056        self.kind.clone()
1057    }
1058
1059    pub(crate) fn is_positional(&self) -> bool {
1060        self.is_positional
1061    }
1062
1063    pub(crate) fn casing(&self) -> Sp<CasingStyle> {
1064        self.casing
1065    }
1066
1067    pub(crate) fn env_casing(&self) -> Sp<CasingStyle> {
1068        self.env_casing
1069    }
1070
1071    pub(crate) fn has_explicit_methods(&self) -> bool {
1072        self.methods
1073            .iter()
1074            .any(|m| m.name != "help" && m.name != "long_help")
1075    }
1076
1077    pub(crate) fn skip_group(&self) -> bool {
1078        self.skip_group
1079    }
1080}
1081
1082#[derive(Clone)]
1083enum ValueParser {
1084    Explicit(Method),
1085    Implicit(Ident),
1086}
1087
1088impl ValueParser {
1089    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
1096    fn span(&self) -> Span {
1097        match self {
1098            Self::Explicit(method) => method.name.span(),
1099            Self::Implicit(ident) => ident.span(),
1100        }
1101    }
1102}
1103
1104fn 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)]
1115pub(crate) enum Action {
1116    Explicit(Method),
1117    Implicit(Ident),
1118}
1119
1120impl Action {
1121    pub(crate) 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
1128    pub(crate) fn span(&self) -> Span {
1129        match self {
1130            Self::Explicit(method) => method.name.span(),
1131            Self::Implicit(ident) => ident.span(),
1132        }
1133    }
1134}
1135
1136fn 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)]
1168pub(crate) 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
1179impl Kind {
1180    pub(crate) 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
1193    pub(crate) 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
1206    pub(crate) 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)]
1219pub(crate) struct Method {
1220    name: Ident,
1221    args: TokenStream,
1222}
1223
1224impl Method {
1225    pub(crate) fn new(name: Ident, args: TokenStream) -> Self {
1226        Method { name, args }
1227    }
1228
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
1256    pub(crate) fn args(&self) -> &TokenStream {
1257        &self.args
1258    }
1259}
1260
1261impl ToTokens for Method {
1262    fn to_tokens(&self, ts: &mut 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)]
1272pub(crate) struct Deprecation {
1273    pub(crate) span: Span,
1274    pub(crate) id: &'static str,
1275    pub(crate) version: &'static str,
1276    pub(crate) description: String,
1277}
1278
1279impl Deprecation {
1280    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
1294impl ToTokens for Deprecation {
1295    fn to_tokens(&self, ts: &mut 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
1319fn 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"`
1342fn 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)]
1365pub(crate) 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
1384impl CasingStyle {
1385    fn from_lit(name: &LitStr) -> Result<Sp<Self>, syn::Error> {
1386        use self::CasingStyle::{
1387            Camel, Kebab, Lower, Pascal, ScreamingSnake, Snake, Upper, Verbatim,
1388        };
1389
1390        let normalized = name.value().to_upper_camel_case().to_lowercase();
1391        let cs = |kind| Sp::new(kind, name.span());
1392
1393        let s = match normalized.as_ref() {
1394            "camel" | "camelcase" => cs(Camel),
1395            "kebab" | "kebabcase" => cs(Kebab),
1396            "pascal" | "pascalcase" => cs(Pascal),
1397            "screamingsnake" | "screamingsnakecase" => cs(ScreamingSnake),
1398            "snake" | "snakecase" => cs(Snake),
1399            "lower" | "lowercase" => cs(Lower),
1400            "upper" | "uppercase" => cs(Upper),
1401            "verbatim" | "verbatimcase" => cs(Verbatim),
1402            s => abort!(name, "unsupported casing: `{s}`"),
1403        };
1404        Ok(s)
1405    }
1406}
1407
1408#[derive(Clone)]
1409pub(crate) enum Name {
1410    Derived(Ident),
1411    Assigned(TokenStream),
1412}
1413
1414impl Name {
1415    pub(crate) fn translate(self, style: CasingStyle) -> TokenStream {
1416        use CasingStyle::{Camel, Kebab, Lower, Pascal, ScreamingSnake, Snake, Upper, Verbatim};
1417
1418        match self {
1419            Name::Assigned(tokens) => tokens,
1420            Name::Derived(ident) => {
1421                let s = ident.unraw().to_string();
1422                let s = match style {
1423                    Pascal => s.to_upper_camel_case(),
1424                    Kebab => s.to_kebab_case(),
1425                    Camel => s.to_lower_camel_case(),
1426                    ScreamingSnake => s.to_shouty_snake_case(),
1427                    Snake => s.to_snake_case(),
1428                    Lower => s.to_snake_case().replace('_', ""),
1429                    Upper => s.to_shouty_snake_case().replace('_', ""),
1430                    Verbatim => s,
1431                };
1432                quote_spanned!(ident.span()=> #s)
1433            }
1434        }
1435    }
1436
1437    pub(crate) fn translate_char(self, style: CasingStyle) -> TokenStream {
1438        use CasingStyle::{Camel, Kebab, Lower, Pascal, ScreamingSnake, Snake, Upper, Verbatim};
1439
1440        match self {
1441            Name::Assigned(tokens) => quote!( (#tokens).chars().next().unwrap() ),
1442            Name::Derived(ident) => {
1443                let s = ident.unraw().to_string();
1444                let s = match style {
1445                    Pascal => s.to_upper_camel_case(),
1446                    Kebab => s.to_kebab_case(),
1447                    Camel => s.to_lower_camel_case(),
1448                    ScreamingSnake => s.to_shouty_snake_case(),
1449                    Snake => s.to_snake_case(),
1450                    Lower => s.to_snake_case(),
1451                    Upper => s.to_shouty_snake_case(),
1452                    Verbatim => s,
1453                };
1454
1455                let s = s.chars().next().unwrap();
1456                quote_spanned!(ident.span()=> #s)
1457            }
1458        }
1459    }
1460}
1461
1462impl ToTokens for Name {
1463    fn to_tokens(&self, tokens: &mut TokenStream) {
1464        match self {
1465            Name::Assigned(t) => t.to_tokens(tokens),
1466            Name::Derived(ident) => {
1467                let s = ident.unraw().to_string();
1468                quote_spanned!(ident.span()=> #s).to_tokens(tokens);
1469            }
1470        }
1471    }
1472}