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::*;
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 const DEFAULT_CASING: CasingStyle = CasingStyle::Kebab;
28
29/// Default casing style for environment variables
30pub const DEFAULT_ENV_CASING: CasingStyle = CasingStyle::ScreamingSnake;
31
32#[derive(Clone)]
33pub 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 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 fn from_subcommand_enum(input: &DeriveInput, name: Name) -> Result<Self, syn::Error> {
74 let ident = input.ident.clone();
75 let span = input.ident.span();
76 let attrs = &input.attrs;
77 let argument_casing = Sp::new(DEFAULT_CASING, span);
78 let env_casing = Sp::new(DEFAULT_ENV_CASING, span);
79 let kind = Sp::new(Kind::Command(Sp::new(Ty::Other, span)), span);
80
81 let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind);
82 let parsed_attrs = ClapAttr::parse_all(attrs)?;
83 res.infer_kind(&parsed_attrs)?;
84 res.push_attrs(&parsed_attrs)?;
85 res.push_doc_comment(attrs, "about", Some("long_about"));
86
87 Ok(res)
88 }
89
90 pub fn from_value_enum(input: &DeriveInput, name: Name) -> Result<Self, syn::Error> {
91 let ident = input.ident.clone();
92 let span = input.ident.span();
93 let attrs = &input.attrs;
94 let argument_casing = Sp::new(DEFAULT_CASING, span);
95 let env_casing = Sp::new(DEFAULT_ENV_CASING, span);
96 let kind = Sp::new(Kind::Value, span);
97
98 let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind);
99 let parsed_attrs = ClapAttr::parse_all(attrs)?;
100 res.infer_kind(&parsed_attrs)?;
101 res.push_attrs(&parsed_attrs)?;
102 // Ignoring `push_doc_comment` as there is no top-level clap builder to add documentation
103 // to
104
105 if res.has_explicit_methods() {
106 abort!(
107 res.methods[0].name.span(),
108 "{} doesn't exist for `ValueEnum` enums",
109 res.methods[0].name
110 );
111 }
112
113 Ok(res)
114 }
115
116 pub fn from_subcommand_variant(
117 variant: &Variant,
118 struct_casing: Sp<CasingStyle>,
119 env_casing: Sp<CasingStyle>,
120 ) -> Result<Self, syn::Error> {
121 let name = variant.ident.clone();
122 let ident = variant.ident.clone();
123 let span = variant.span();
124 let ty = match variant.fields {
125 syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
126 Ty::from_syn_ty(&unnamed[0].ty)
127 }
128 syn::Fields::Named(_) | syn::Fields::Unnamed(..) | syn::Fields::Unit => {
129 Sp::new(Ty::Other, span)
130 }
131 };
132 let kind = Sp::new(Kind::Command(ty), span);
133 let mut res = Self::new(
134 Name::Derived(name),
135 ident,
136 None,
137 struct_casing,
138 env_casing,
139 kind,
140 );
141 let parsed_attrs = ClapAttr::parse_all(&variant.attrs)?;
142 res.infer_kind(&parsed_attrs)?;
143 res.push_attrs(&parsed_attrs)?;
144 if matches!(&*res.kind, Kind::Command(_) | Kind::Subcommand(_)) {
145 res.push_doc_comment(&variant.attrs, "about", Some("long_about"));
146 }
147
148 match &*res.kind {
149 Kind::Flatten(_) => {
150 if res.has_explicit_methods() {
151 abort!(
152 res.kind.span(),
153 "methods are not allowed for flattened entry"
154 );
155 }
156 }
157
158 Kind::Subcommand(_)
159 | Kind::ExternalSubcommand
160 | Kind::FromGlobal(_)
161 | Kind::Skip(_, _)
162 | Kind::Command(_)
163 | Kind::Value
164 | Kind::Arg(_) => (),
165 }
166
167 Ok(res)
168 }
169
170 pub fn from_value_enum_variant(
171 variant: &Variant,
172 argument_casing: Sp<CasingStyle>,
173 env_casing: Sp<CasingStyle>,
174 ) -> Result<Self, syn::Error> {
175 let ident = variant.ident.clone();
176 let span = variant.span();
177 let kind = Sp::new(Kind::Value, span);
178 let mut res = Self::new(
179 Name::Derived(variant.ident.clone()),
180 ident,
181 None,
182 argument_casing,
183 env_casing,
184 kind,
185 );
186 let parsed_attrs = ClapAttr::parse_all(&variant.attrs)?;
187 res.infer_kind(&parsed_attrs)?;
188 res.push_attrs(&parsed_attrs)?;
189 if matches!(&*res.kind, Kind::Value) {
190 res.push_doc_comment(&variant.attrs, "help", None);
191 }
192
193 Ok(res)
194 }
195
196 pub fn from_args_field(
197 field: &Field,
198 struct_casing: Sp<CasingStyle>,
199 env_casing: Sp<CasingStyle>,
200 ) -> Result<Self, syn::Error> {
201 let name = field.ident.clone().unwrap();
202 let ident = field.ident.clone().unwrap();
203 let span = field.span();
204 let ty = Ty::from_syn_ty(&field.ty);
205 let kind = Sp::new(Kind::Arg(ty), span);
206 let mut res = Self::new(
207 Name::Derived(name),
208 ident,
209 Some(field.ty.clone()),
210 struct_casing,
211 env_casing,
212 kind,
213 );
214 let parsed_attrs = ClapAttr::parse_all(&field.attrs)?;
215 res.infer_kind(&parsed_attrs)?;
216 res.push_attrs(&parsed_attrs)?;
217 if matches!(&*res.kind, Kind::Arg(_)) {
218 res.push_doc_comment(&field.attrs, "help", Some("long_help"));
219 }
220
221 match &*res.kind {
222 Kind::Flatten(_) => {
223 if res.has_explicit_methods() {
224 abort!(
225 res.kind.span(),
226 "methods are not allowed for flattened entry"
227 );
228 }
229 }
230
231 Kind::Subcommand(_) => {
232 if res.has_explicit_methods() {
233 abort!(
234 res.kind.span(),
235 "methods in attributes are not allowed for subcommand"
236 );
237 }
238 }
239 Kind::Skip(_, _)
240 | Kind::FromGlobal(_)
241 | Kind::Arg(_)
242 | Kind::Command(_)
243 | Kind::Value
244 | Kind::ExternalSubcommand => {}
245 }
246
247 Ok(res)
248 }
249
250 fn new(
251 name: Name,
252 ident: Ident,
253 ty: Option<Type>,
254 casing: Sp<CasingStyle>,
255 env_casing: Sp<CasingStyle>,
256 kind: Sp<Kind>,
257 ) -> Self {
258 let group_id = Name::Derived(ident);
259 Self {
260 name,
261 ty,
262 casing,
263 env_casing,
264 doc_comment: vec![],
265 methods: vec![],
266 deprecations: vec![],
267 value_parser: None,
268 action: None,
269 verbatim_doc_comment: false,
270 force_long_help: false,
271 next_display_order: None,
272 next_help_heading: None,
273 is_enum: false,
274 is_positional: true,
275 skip_group: false,
276 group_id,
277 group_methods: vec![],
278 kind,
279 }
280 }
281
282 fn push_method(&mut self, kind: AttrKind, name: Ident, arg: impl ToTokens) {
283 self.push_method_(kind, name, arg.to_token_stream());
284 }
285
286 fn push_method_(&mut self, kind: AttrKind, name: Ident, arg: TokenStream) {
287 if name == "id" {
288 match kind {
289 AttrKind::Command | AttrKind::Value => {
290 self.deprecations.push(Deprecation {
291 span: name.span(),
292 id: "id_is_only_for_arg",
293 version: "4.0.0",
294 description: format!(
295 "`#[{}(id)] was allowed by mistake, instead use `#[{}(name)]`",
296 kind.as_str(),
297 kind.as_str()
298 ),
299 });
300 self.name = Name::Assigned(arg);
301 }
302 AttrKind::Group => {
303 self.group_id = Name::Assigned(arg);
304 }
305 AttrKind::Arg | AttrKind::Clap | AttrKind::StructOpt => {
306 self.name = Name::Assigned(arg);
307 }
308 }
309 } else if name == "name" {
310 match kind {
311 AttrKind::Arg => {
312 self.deprecations.push(Deprecation {
313 span: name.span(),
314 id: "id_is_only_for_arg",
315 version: "4.0.0",
316 description: format!(
317 "`#[{}(name)] was allowed by mistake, instead use `#[{}(id)]` or `#[{}(value_name)]`",
318 kind.as_str(),
319 kind.as_str(),
320 kind.as_str()
321 ),
322 });
323 self.name = Name::Assigned(arg);
324 }
325 AttrKind::Group => self.group_methods.push(Method::new(name, arg)),
326 AttrKind::Command | AttrKind::Value | AttrKind::Clap | AttrKind::StructOpt => {
327 self.name = Name::Assigned(arg);
328 }
329 }
330 } else if name == "value_parser" {
331 self.value_parser = Some(ValueParser::Explicit(Method::new(name, arg)));
332 } else if name == "action" {
333 self.action = Some(Action::Explicit(Method::new(name, arg)));
334 } else {
335 if name == "short" || name == "long" {
336 self.is_positional = false;
337 }
338 match kind {
339 AttrKind::Group => self.group_methods.push(Method::new(name, arg)),
340 _ => self.methods.push(Method::new(name, arg)),
341 };
342 }
343 }
344
345 fn infer_kind(&mut self, attrs: &[ClapAttr]) -> Result<(), syn::Error> {
346 for attr in attrs {
347 if let Some(AttrValue::Call(_)) = &attr.value {
348 continue;
349 }
350
351 let actual_attr_kind = *attr.kind.get();
352 let kind = match &attr.magic {
353 Some(MagicAttrName::FromGlobal) => {
354 if attr.value.is_some() {
355 let expr = attr.value_or_abort()?;
356 abort!(expr, "attribute `{}` does not accept a value", attr.name);
357 }
358 let ty = self
359 .kind()
360 .ty()
361 .cloned()
362 .unwrap_or_else(|| Sp::new(Ty::Other, self.kind.span()));
363 let kind = Sp::new(Kind::FromGlobal(ty), attr.name.clone().span());
364 Some(kind)
365 }
366 Some(MagicAttrName::Subcommand) if attr.value.is_none() => {
367 if attr.value.is_some() {
368 let expr = attr.value_or_abort()?;
369 abort!(expr, "attribute `{}` does not accept a value", attr.name);
370 }
371 let ty = self
372 .kind()
373 .ty()
374 .cloned()
375 .unwrap_or_else(|| Sp::new(Ty::Other, self.kind.span()));
376 let kind = Sp::new(Kind::Subcommand(ty), attr.name.clone().span());
377 Some(kind)
378 }
379 Some(MagicAttrName::ExternalSubcommand) if attr.value.is_none() => {
380 if attr.value.is_some() {
381 let expr = attr.value_or_abort()?;
382 abort!(expr, "attribute `{}` does not accept a value", attr.name);
383 }
384 let kind = Sp::new(Kind::ExternalSubcommand, attr.name.clone().span());
385 Some(kind)
386 }
387 Some(MagicAttrName::Flatten) if attr.value.is_none() => {
388 if attr.value.is_some() {
389 let expr = attr.value_or_abort()?;
390 abort!(expr, "attribute `{}` does not accept a value", attr.name);
391 }
392 let ty = self
393 .kind()
394 .ty()
395 .cloned()
396 .unwrap_or_else(|| Sp::new(Ty::Other, self.kind.span()));
397 let kind = Sp::new(Kind::Flatten(ty), attr.name.clone().span());
398 Some(kind)
399 }
400 Some(MagicAttrName::Skip) if actual_attr_kind != AttrKind::Group => {
401 let expr = attr.value.clone();
402 let kind = Sp::new(
403 Kind::Skip(expr, self.kind.attr_kind()),
404 attr.name.clone().span(),
405 );
406 Some(kind)
407 }
408 _ => None,
409 };
410
411 if let Some(kind) = kind {
412 self.set_kind(kind)?;
413 }
414 }
415
416 Ok(())
417 }
418
419 fn push_attrs(&mut self, attrs: &[ClapAttr]) -> Result<(), syn::Error> {
420 for attr in attrs {
421 let actual_attr_kind = *attr.kind.get();
422 let expected_attr_kind = self.kind.attr_kind();
423 match (actual_attr_kind, expected_attr_kind) {
424 (AttrKind::Clap, _) | (AttrKind::StructOpt, _) => {
425 self.deprecations.push(Deprecation::attribute(
426 "4.0.0",
427 actual_attr_kind,
428 expected_attr_kind,
429 attr.kind.span(),
430 ));
431 }
432
433 (AttrKind::Group, AttrKind::Command) => {}
434
435 _ if attr.kind != expected_attr_kind => {
436 abort!(
437 attr.kind.span(),
438 "Expected `{}` attribute instead of `{}`",
439 expected_attr_kind.as_str(),
440 actual_attr_kind.as_str()
441 );
442 }
443
444 _ => {}
445 }
446
447 if let Some(AttrValue::Call(tokens)) = &attr.value {
448 // Force raw mode with method call syntax
449 self.push_method(*attr.kind.get(), attr.name.clone(), quote!(#(#tokens),*));
450 continue;
451 }
452
453 match &attr.magic {
454 Some(MagicAttrName::Short) if attr.value.is_none() => {
455 assert_attr_kind(attr, &[AttrKind::Arg])?;
456
457 self.push_method(
458 *attr.kind.get(),
459 attr.name.clone(),
460 self.name.clone().translate_char(*self.casing),
461 );
462 }
463
464 Some(MagicAttrName::Long) if attr.value.is_none() => {
465 assert_attr_kind(attr, &[AttrKind::Arg])?;
466
467 self.push_method(*attr.kind.get(), attr.name.clone(), self.name.clone().translate(*self.casing));
468 }
469
470 Some(MagicAttrName::ValueParser) if attr.value.is_none() => {
471 assert_attr_kind(attr, &[AttrKind::Arg])?;
472
473 self.deprecations.push(Deprecation {
474 span: attr.name.span(),
475 id: "bare_value_parser",
476 version: "4.0.0",
477 description: "`#[arg(value_parser)]` is now the default and is no longer needed`".to_owned(),
478 });
479 self.value_parser = Some(ValueParser::Implicit(attr.name.clone()));
480 }
481
482 Some(MagicAttrName::Action) if attr.value.is_none() => {
483 assert_attr_kind(attr, &[AttrKind::Arg])?;
484
485 self.deprecations.push(Deprecation {
486 span: attr.name.span(),
487 id: "bare_action",
488 version: "4.0.0",
489 description: "`#[arg(action)]` is now the default and is no longer needed`".to_owned(),
490 });
491 self.action = Some(Action::Implicit(attr.name.clone()));
492 }
493
494 Some(MagicAttrName::Env) if attr.value.is_none() => {
495 assert_attr_kind(attr, &[AttrKind::Arg])?;
496
497 self.push_method(
498 *attr.kind.get(),
499 attr.name.clone(),
500 self.name.clone().translate(*self.env_casing),
501 );
502 }
503
504 Some(MagicAttrName::ValueEnum) if attr.value.is_none() => {
505 assert_attr_kind(attr, &[AttrKind::Arg])?;
506
507 self.is_enum = true
508 }
509
510 Some(MagicAttrName::VerbatimDocComment) if attr.value.is_none() => {
511 self.verbatim_doc_comment = true
512 }
513
514 Some(MagicAttrName::About) if attr.value.is_none() => {
515 assert_attr_kind(attr, &[AttrKind::Command])?;
516
517 if let Some(method) =
518 Method::from_env(attr.name.clone(), "CARGO_PKG_DESCRIPTION")?
519 {
520 self.methods.push(method);
521 }
522 }
523
524 Some(MagicAttrName::LongAbout) if attr.value.is_none() => {
525 assert_attr_kind(attr, &[AttrKind::Command])?;
526
527 self.force_long_help = true;
528 }
529
530 Some(MagicAttrName::LongHelp) if attr.value.is_none() => {
531 assert_attr_kind(attr, &[AttrKind::Arg])?;
532
533 self.force_long_help = true;
534 }
535
536 Some(MagicAttrName::Author) if attr.value.is_none() => {
537 assert_attr_kind(attr, &[AttrKind::Command])?;
538
539 if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_AUTHORS")? {
540 self.methods.push(method);
541 }
542 }
543
544 Some(MagicAttrName::Version) if attr.value.is_none() => {
545 assert_attr_kind(attr, &[AttrKind::Command])?;
546
547 if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_VERSION")? {
548 self.methods.push(method);
549 }
550 }
551
552 Some(MagicAttrName::DefaultValueT) => {
553 assert_attr_kind(attr, &[AttrKind::Arg])?;
554
555 let ty = if let Some(ty) = self.ty.as_ref() {
556 ty
557 } else {
558 abort!(
559 attr.name.clone(),
560 "#[arg(default_value_t)] (without an argument) can be used \
561 only on field level\n\n= note: {note}\n\n",
562
563 note = "see \
564 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
565 };
566
567 let val = if let Some(expr) = &attr.value {
568 quote!(#expr)
569 } else {
570 quote!(<#ty as ::std::default::Default>::default())
571 };
572
573 let val = if attrs
574 .iter()
575 .any(|a| a.magic == Some(MagicAttrName::ValueEnum))
576 {
577 quote_spanned!(attr.name.clone().span()=> {
578 static DEFAULT_VALUE: ::std::sync::OnceLock<String> = ::std::sync::OnceLock::new();
579 let s = DEFAULT_VALUE.get_or_init(|| {
580 let val: #ty = #val;
581 clap::ValueEnum::to_possible_value(&val).unwrap().get_name().to_owned()
582 });
583 let s: &'static str = &*s;
584 s
585 })
586 } else {
587 quote_spanned!(attr.name.clone().span()=> {
588 static DEFAULT_VALUE: ::std::sync::OnceLock<String> = ::std::sync::OnceLock::new();
589 let s = DEFAULT_VALUE.get_or_init(|| {
590 let val: #ty = #val;
591 ::std::string::ToString::to_string(&val)
592 });
593 let s: &'static str = &*s;
594 s
595 })
596 };
597
598 let raw_ident = Ident::new("default_value", attr.name.clone().span());
599 self.methods.push(Method::new(raw_ident, val));
600 }
601
602 Some(MagicAttrName::DefaultValuesT) => {
603 assert_attr_kind(attr, &[AttrKind::Arg])?;
604
605 let ty = if let Some(ty) = self.ty.as_ref() {
606 ty
607 } else {
608 abort!(
609 attr.name.clone(),
610 "#[arg(default_values_t)] (without an argument) can be used \
611 only on field level\n\n= note: {note}\n\n",
612
613 note = "see \
614 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
615 };
616 let expr = attr.value_or_abort()?;
617
618 let container_type = Ty::from_syn_ty(ty);
619 if *container_type != Ty::Vec {
620 abort!(
621 attr.name.clone(),
622 "#[arg(default_values_t)] can be used only on Vec types\n\n= note: {note}\n\n",
623
624 note = "see \
625 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
626 }
627 let inner_type = inner_type(ty);
628
629 // Use `Borrow<#inner_type>` so we accept `&Vec<#inner_type>` and
630 // `Vec<#inner_type>`.
631 let val = if attrs
632 .iter()
633 .any(|a| a.magic == Some(MagicAttrName::ValueEnum))
634 {
635 quote_spanned!(attr.name.clone().span()=> {
636 {
637 fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=String>
638 where
639 T: ::std::borrow::Borrow<#inner_type>
640 {
641 iterable
642 .into_iter()
643 .map(|val| {
644 clap::ValueEnum::to_possible_value(val.borrow()).unwrap().get_name().to_owned()
645 })
646 }
647
648 static DEFAULT_STRINGS: ::std::sync::OnceLock<Vec<String>> = ::std::sync::OnceLock::new();
649 static DEFAULT_VALUES: ::std::sync::OnceLock<Vec<&str>> = ::std::sync::OnceLock::new();
650 DEFAULT_VALUES.get_or_init(|| {
651 DEFAULT_STRINGS.get_or_init(|| iter_to_vals(#expr).collect()).iter().map(::std::string::String::as_str).collect()
652 }).iter().copied()
653 }
654 })
655 } else {
656 quote_spanned!(attr.name.clone().span()=> {
657 {
658 fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=String>
659 where
660 T: ::std::borrow::Borrow<#inner_type>
661 {
662 iterable.into_iter().map(|val| val.borrow().to_string())
663 }
664
665 static DEFAULT_STRINGS: ::std::sync::OnceLock<Vec<String>> = ::std::sync::OnceLock::new();
666 static DEFAULT_VALUES: ::std::sync::OnceLock<Vec<&str>> = ::std::sync::OnceLock::new();
667 DEFAULT_VALUES.get_or_init(|| {
668 DEFAULT_STRINGS.get_or_init(|| iter_to_vals(#expr).collect()).iter().map(::std::string::String::as_str).collect()
669 }).iter().copied()
670 }
671 })
672 };
673
674 self.methods.push(Method::new(
675 Ident::new("default_values", attr.name.clone().span()),
676 val,
677 ));
678 }
679
680 Some(MagicAttrName::DefaultValueOsT) => {
681 assert_attr_kind(attr, &[AttrKind::Arg])?;
682
683 let ty = if let Some(ty) = self.ty.as_ref() {
684 ty
685 } else {
686 abort!(
687 attr.name.clone(),
688 "#[arg(default_value_os_t)] (without an argument) can be used \
689 only on field level\n\n= note: {note}\n\n",
690
691 note = "see \
692 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
693 };
694
695 let val = if let Some(expr) = &attr.value {
696 quote!(#expr)
697 } else {
698 quote!(<#ty as ::std::default::Default>::default())
699 };
700
701 let val = if attrs
702 .iter()
703 .any(|a| a.magic == Some(MagicAttrName::ValueEnum))
704 {
705 quote_spanned!(attr.name.clone().span()=> {
706 static DEFAULT_VALUE: ::std::sync::OnceLock<String> = ::std::sync::OnceLock::new();
707 let s = DEFAULT_VALUE.get_or_init(|| {
708 let val: #ty = #val;
709 clap::ValueEnum::to_possible_value(&val).unwrap().get_name().to_owned()
710 });
711 let s: &'static str = &*s;
712 s
713 })
714 } else {
715 quote_spanned!(attr.name.clone().span()=> {
716 static DEFAULT_VALUE: ::std::sync::OnceLock<::std::ffi::OsString> = ::std::sync::OnceLock::new();
717 let s = DEFAULT_VALUE.get_or_init(|| {
718 let val: #ty = #val;
719 ::std::ffi::OsString::from(val)
720 });
721 let s: &'static ::std::ffi::OsStr = &*s;
722 s
723 })
724 };
725
726 let raw_ident = Ident::new("default_value", attr.name.clone().span());
727 self.methods.push(Method::new(raw_ident, val));
728 }
729
730 Some(MagicAttrName::DefaultValuesOsT) => {
731 assert_attr_kind(attr, &[AttrKind::Arg])?;
732
733 let ty = if let Some(ty) = self.ty.as_ref() {
734 ty
735 } else {
736 abort!(
737 attr.name.clone(),
738 "#[arg(default_values_os_t)] (without an argument) can be used \
739 only on field level\n\n= note: {note}\n\n",
740
741 note = "see \
742 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
743 };
744 let expr = attr.value_or_abort()?;
745
746 let container_type = Ty::from_syn_ty(ty);
747 if *container_type != Ty::Vec {
748 abort!(
749 attr.name.clone(),
750 "#[arg(default_values_os_t)] can be used only on Vec types\n\n= note: {note}\n\n",
751
752 note = "see \
753 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
754 }
755 let inner_type = inner_type(ty);
756
757 // Use `Borrow<#inner_type>` so we accept `&Vec<#inner_type>` and
758 // `Vec<#inner_type>`.
759 let val = if attrs
760 .iter()
761 .any(|a| a.magic == Some(MagicAttrName::ValueEnum))
762 {
763 quote_spanned!(attr.name.clone().span()=> {
764 {
765 fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=::std::ffi::OsString>
766 where
767 T: ::std::borrow::Borrow<#inner_type>
768 {
769 iterable
770 .into_iter()
771 .map(|val| {
772 clap::ValueEnum::to_possible_value(val.borrow()).unwrap().get_name().to_owned().into()
773 })
774 }
775
776 static DEFAULT_STRINGS: ::std::sync::OnceLock<Vec<::std::ffi::OsString>> = ::std::sync::OnceLock::new();
777 static DEFAULT_VALUES: ::std::sync::OnceLock<Vec<&::std::ffi::OsStr>> = ::std::sync::OnceLock::new();
778 DEFAULT_VALUES.get_or_init(|| {
779 DEFAULT_STRINGS.get_or_init(|| iter_to_vals(#expr).collect()).iter().map(::std::ffi::OsString::as_os_str).collect()
780 }).iter().copied()
781 }
782 })
783 } else {
784 quote_spanned!(attr.name.clone().span()=> {
785 {
786 fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=::std::ffi::OsString>
787 where
788 T: ::std::borrow::Borrow<#inner_type>
789 {
790 iterable.into_iter().map(|val| val.borrow().into())
791 }
792
793 static DEFAULT_STRINGS: ::std::sync::OnceLock<Vec<::std::ffi::OsString>> = ::std::sync::OnceLock::new();
794 static DEFAULT_VALUES: ::std::sync::OnceLock<Vec<&::std::ffi::OsStr>> = ::std::sync::OnceLock::new();
795 DEFAULT_VALUES.get_or_init(|| {
796 DEFAULT_STRINGS.get_or_init(|| iter_to_vals(#expr).collect()).iter().map(::std::ffi::OsString::as_os_str).collect()
797 }).iter().copied()
798 }
799 })
800 };
801
802 self.methods.push(Method::new(
803 Ident::new("default_values", attr.name.clone().span()),
804 val,
805 ));
806 }
807
808 Some(MagicAttrName::NextDisplayOrder) => {
809 assert_attr_kind(attr, &[AttrKind::Command])?;
810
811 let expr = attr.value_or_abort()?;
812 self.next_display_order = Some(Method::new(attr.name.clone(), quote!(#expr)));
813 }
814
815 Some(MagicAttrName::NextHelpHeading) => {
816 assert_attr_kind(attr, &[AttrKind::Command])?;
817
818 let expr = attr.value_or_abort()?;
819 self.next_help_heading = Some(Method::new(attr.name.clone(), quote!(#expr)));
820 }
821
822 Some(MagicAttrName::RenameAll) => {
823 let lit = attr.lit_str_or_abort()?;
824 self.casing = CasingStyle::from_lit(lit)?;
825 }
826
827 Some(MagicAttrName::RenameAllEnv) => {
828 assert_attr_kind(attr, &[AttrKind::Command, AttrKind::Arg])?;
829
830 let lit = attr.lit_str_or_abort()?;
831 self.env_casing = CasingStyle::from_lit(lit)?;
832 }
833
834 Some(MagicAttrName::Skip) if actual_attr_kind == AttrKind::Group => {
835 self.skip_group = true;
836 }
837
838 None
839 // Magic only for the default, otherwise just forward to the builder
840 | Some(MagicAttrName::Short)
841 | Some(MagicAttrName::Long)
842 | Some(MagicAttrName::Env)
843 | Some(MagicAttrName::About)
844 | Some(MagicAttrName::LongAbout)
845 | Some(MagicAttrName::LongHelp)
846 | Some(MagicAttrName::Author)
847 | Some(MagicAttrName::Version)
848 => {
849 let expr = attr.value_or_abort()?;
850 self.push_method(*attr.kind.get(), attr.name.clone(), expr);
851 }
852
853 // Magic only for the default, otherwise just forward to the builder
854 Some(MagicAttrName::ValueParser) | Some(MagicAttrName::Action) => {
855 let expr = attr.value_or_abort()?;
856 self.push_method(*attr.kind.get(), attr.name.clone(), expr);
857 }
858
859 // Directives that never receive a value
860 Some(MagicAttrName::ValueEnum)
861 | Some(MagicAttrName::VerbatimDocComment) => {
862 let expr = attr.value_or_abort()?;
863 abort!(expr, "attribute `{}` does not accept a value", attr.name);
864 }
865
866 // Kinds
867 Some(MagicAttrName::FromGlobal)
868 | Some(MagicAttrName::Subcommand)
869 | Some(MagicAttrName::ExternalSubcommand)
870 | Some(MagicAttrName::Flatten)
871 | Some(MagicAttrName::Skip) => {
872 }
873 }
874 }
875
876 if self.has_explicit_methods() {
877 if let Kind::Skip(_, attr) = &*self.kind {
878 abort!(
879 self.methods[0].name.span(),
880 "`{}` cannot be used with `#[{}(skip)]",
881 self.methods[0].name,
882 attr.as_str(),
883 );
884 }
885 if let Kind::FromGlobal(_) = &*self.kind {
886 abort!(
887 self.methods[0].name.span(),
888 "`{}` cannot be used with `#[arg(from_global)]",
889 self.methods[0].name,
890 );
891 }
892 }
893
894 Ok(())
895 }
896
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 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 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 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 fn field_methods(&self) -> proc_macro2::TokenStream {
972 let methods = &self.methods;
973 let doc_comment = &self.doc_comment;
974 quote!( #(#doc_comment)* #(#methods)* )
975 }
976
977 pub fn group_id(&self) -> TokenStream {
978 self.group_id.clone().raw()
979 }
980
981 pub fn group_methods(&self) -> TokenStream {
982 let group_methods = &self.group_methods;
983 quote!( #(#group_methods)* )
984 }
985
986 pub fn deprecations(&self) -> proc_macro2::TokenStream {
987 let deprecations = &self.deprecations;
988 quote!( #(#deprecations)* )
989 }
990
991 pub fn next_display_order(&self) -> TokenStream {
992 let next_display_order = self.next_display_order.as_ref().into_iter();
993 quote!( #(#next_display_order)* )
994 }
995
996 pub fn next_help_heading(&self) -> TokenStream {
997 let next_help_heading = self.next_help_heading.as_ref().into_iter();
998 quote!( #(#next_help_heading)* )
999 }
1000
1001 pub fn id(&self) -> TokenStream {
1002 self.name.clone().raw()
1003 }
1004
1005 pub fn cased_name(&self) -> TokenStream {
1006 self.name.clone().translate(*self.casing)
1007 }
1008
1009 pub fn value_name(&self) -> TokenStream {
1010 self.name.clone().translate(CasingStyle::ScreamingSnake)
1011 }
1012
1013 pub fn value_parser(&self, field_type: &Type) -> Method {
1014 self.value_parser
1015 .clone()
1016 .map(|p| {
1017 let inner_type = inner_type(field_type);
1018 p.resolve(inner_type)
1019 })
1020 .unwrap_or_else(|| {
1021 let inner_type = inner_type(field_type);
1022 if let Some(action) = self.action.as_ref() {
1023 let span = action.span();
1024 default_value_parser(inner_type, span)
1025 } else {
1026 let span = self
1027 .action
1028 .as_ref()
1029 .map(|a| a.span())
1030 .unwrap_or_else(|| self.kind.span());
1031 default_value_parser(inner_type, span)
1032 }
1033 })
1034 }
1035
1036 pub fn action(&self, field_type: &Type) -> Method {
1037 self.action
1038 .clone()
1039 .map(|p| p.resolve(field_type))
1040 .unwrap_or_else(|| {
1041 if let Some(value_parser) = self.value_parser.as_ref() {
1042 let span = value_parser.span();
1043 default_action(field_type, span)
1044 } else {
1045 let span = self
1046 .value_parser
1047 .as_ref()
1048 .map(|a| a.span())
1049 .unwrap_or_else(|| self.kind.span());
1050 default_action(field_type, span)
1051 }
1052 })
1053 }
1054
1055 pub fn kind(&self) -> Sp<Kind> {
1056 self.kind.clone()
1057 }
1058
1059 pub fn is_positional(&self) -> bool {
1060 self.is_positional
1061 }
1062
1063 pub fn casing(&self) -> Sp<CasingStyle> {
1064 self.casing
1065 }
1066
1067 pub fn env_casing(&self) -> Sp<CasingStyle> {
1068 self.env_casing
1069 }
1070
1071 pub 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 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) => method,
1092 Self::Implicit(ident: Ident) => default_value_parser(inner_type:_inner_type, ident.span()),
1093 }
1094 }
1095
1096 fn span(&self) -> Span {
1097 match self {
1098 Self::Explicit(method: &Method) => method.name.span(),
1099 Self::Implicit(ident: &Ident) => ident.span(),
1100 }
1101 }
1102}
1103
1104fn default_value_parser(inner_type: &Type, span: Span) -> Method {
1105 let func: Ident = Ident::new(string:"value_parser", span);
1106 Method::new(
1107 name:func,
1108 args:quote_spanned! { span=>
1109 clap::value_parser!(#inner_type)
1110 },
1111 )
1112}
1113
1114#[derive(Clone)]
1115pub enum Action {
1116 Explicit(Method),
1117 Implicit(Ident),
1118}
1119
1120impl Action {
1121 pub fn resolve(self, _field_type: &Type) -> Method {
1122 match self {
1123 Self::Explicit(method: Method) => method,
1124 Self::Implicit(ident: Ident) => default_action(field_type:_field_type, ident.span()),
1125 }
1126 }
1127
1128 pub fn span(&self) -> Span {
1129 match self {
1130 Self::Explicit(method: &Method) => method.name.span(),
1131 Self::Implicit(ident: &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 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 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 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 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 struct Method {
1220 name: Ident,
1221 args: TokenStream,
1222}
1223
1224impl Method {
1225 pub 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 proc_macro2::TokenStream) {
1263 let Method { ref name: &Ident, ref args: &TokenStream } = self;
1264
1265 let tokens: TokenStream = quote!( .#name(#args) );
1266
1267 tokens.to_tokens(ts);
1268 }
1269}
1270
1271#[derive(Clone)]
1272pub struct Deprecation {
1273 pub span: Span,
1274 pub id: &'static str,
1275 pub version: &'static str,
1276 pub 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 proc_macro2::TokenStream) {
1296 let tokens: TokenStream = if cfg!(feature = "deprecated") {
1297 let Deprecation {
1298 span: &Span,
1299 id: &&str,
1300 version: &&str,
1301 description: &String,
1302 } = self;
1303 let span: Span = *span;
1304 let id: Ident = Ident::new(string: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: Vec = possible_kindimpl Iterator
1324 .iter()
1325 .map(|k: &AttrKind| 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 = String::with_capacity(author.len());
1344 let mut inside_angle_braces: usize = 0usize;
1345
1346 for ch: char 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(string:", ");
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 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
1388 let normalized: String = name.value().to_upper_camel_case().to_lowercase();
1389 let cs: impl Fn(CasingStyle) -> Sp<…> = |kind: CasingStyle| Sp::new(val:kind, name.span());
1390
1391 let s: Sp = match normalized.as_ref() {
1392 "camel" | "camelcase" => cs(kind:Camel),
1393 "kebab" | "kebabcase" => cs(kind:Kebab),
1394 "pascal" | "pascalcase" => cs(kind:Pascal),
1395 "screamingsnake" | "screamingsnakecase" => cs(kind:ScreamingSnake),
1396 "snake" | "snakecase" => cs(kind:Snake),
1397 "lower" | "lowercase" => cs(kind:Lower),
1398 "upper" | "uppercase" => cs(kind:Upper),
1399 "verbatim" | "verbatimcase" => cs(kind:Verbatim),
1400 s: &str => abort!(name, "unsupported casing: `{s}`"),
1401 };
1402 Ok(s)
1403 }
1404}
1405
1406#[derive(Clone)]
1407pub enum Name {
1408 Derived(Ident),
1409 Assigned(TokenStream),
1410}
1411
1412impl Name {
1413 pub fn raw(self) -> TokenStream {
1414 match self {
1415 Name::Assigned(tokens) => tokens,
1416 Name::Derived(ident) => {
1417 let s = ident.unraw().to_string();
1418 quote_spanned!(ident.span()=> #s)
1419 }
1420 }
1421 }
1422
1423 pub fn translate(self, style: CasingStyle) -> TokenStream {
1424 use CasingStyle::*;
1425
1426 match self {
1427 Name::Assigned(tokens) => tokens,
1428 Name::Derived(ident) => {
1429 let s = ident.unraw().to_string();
1430 let s = match style {
1431 Pascal => s.to_upper_camel_case(),
1432 Kebab => s.to_kebab_case(),
1433 Camel => s.to_lower_camel_case(),
1434 ScreamingSnake => s.to_shouty_snake_case(),
1435 Snake => s.to_snake_case(),
1436 Lower => s.to_snake_case().replace('_', ""),
1437 Upper => s.to_shouty_snake_case().replace('_', ""),
1438 Verbatim => s,
1439 };
1440 quote_spanned!(ident.span()=> #s)
1441 }
1442 }
1443 }
1444
1445 pub fn translate_char(self, style: CasingStyle) -> TokenStream {
1446 use CasingStyle::*;
1447
1448 match self {
1449 Name::Assigned(tokens) => quote!( (#tokens).chars().next().unwrap() ),
1450 Name::Derived(ident) => {
1451 let s = ident.unraw().to_string();
1452 let s = match style {
1453 Pascal => s.to_upper_camel_case(),
1454 Kebab => s.to_kebab_case(),
1455 Camel => s.to_lower_camel_case(),
1456 ScreamingSnake => s.to_shouty_snake_case(),
1457 Snake => s.to_snake_case(),
1458 Lower => s.to_snake_case(),
1459 Upper => s.to_shouty_snake_case(),
1460 Verbatim => s,
1461 };
1462
1463 let s = s.chars().next().unwrap();
1464 quote_spanned!(ident.span()=> #s)
1465 }
1466 }
1467 }
1468}
1469