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
15 use std::env;
16
17 use heck::{ToKebabCase, ToLowerCamelCase, ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};
18 use proc_macro2::{self, Span, TokenStream};
19 use proc_macro_error::abort;
20 use quote::{format_ident, quote, quote_spanned, ToTokens};
21 use syn::DeriveInput;
22 use syn::{self, ext::IdentExt, spanned::Spanned, Attribute, Field, Ident, LitStr, Type, Variant};
23
24 use crate::attr::*;
25 use crate::utils::{extract_doc_comment, format_doc_comment, inner_type, is_simple_ty, Sp, Ty};
26
27 /// Default casing style for generated arguments.
28 pub const DEFAULT_CASING: CasingStyle = CasingStyle::Kebab;
29
30 /// Default casing style for environment variables
31 pub const DEFAULT_ENV_CASING: CasingStyle = CasingStyle::ScreamingSnake;
32
33 #[derive(Clone)]
34 pub struct Item {
35 name: Name,
36 ident: Ident,
37 casing: Sp<CasingStyle>,
38 env_casing: Sp<CasingStyle>,
39 ty: Option<Type>,
40 doc_comment: Vec<Method>,
41 methods: Vec<Method>,
42 deprecations: Vec<Deprecation>,
43 value_parser: Option<ValueParser>,
44 action: Option<Action>,
45 verbatim_doc_comment: bool,
46 force_long_help: bool,
47 next_display_order: Option<Method>,
48 next_help_heading: Option<Method>,
49 is_enum: bool,
50 is_positional: bool,
51 skip_group: bool,
52 kind: Sp<Kind>,
53 }
54
55 impl Item {
from_args_struct(input: &DeriveInput, name: Name) -> Self56 pub fn from_args_struct(input: &DeriveInput, name: Name) -> Self {
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 res
71 }
72
from_subcommand_enum(input: &DeriveInput, name: Name) -> Self73 pub fn from_subcommand_enum(input: &DeriveInput, name: Name) -> Self {
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 res
88 }
89
from_value_enum(input: &DeriveInput, name: Name) -> Self90 pub fn from_value_enum(input: &DeriveInput, name: Name) -> Self {
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 res
114 }
115
from_subcommand_variant( variant: &Variant, struct_casing: Sp<CasingStyle>, env_casing: Sp<CasingStyle>, ) -> Self116 pub fn from_subcommand_variant(
117 variant: &Variant,
118 struct_casing: Sp<CasingStyle>,
119 env_casing: Sp<CasingStyle>,
120 ) -> Self {
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 res
168 }
169
from_value_enum_variant( variant: &Variant, argument_casing: Sp<CasingStyle>, env_casing: Sp<CasingStyle>, ) -> Self170 pub fn from_value_enum_variant(
171 variant: &Variant,
172 argument_casing: Sp<CasingStyle>,
173 env_casing: Sp<CasingStyle>,
174 ) -> Self {
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 res
194 }
195
from_args_field( field: &Field, struct_casing: Sp<CasingStyle>, env_casing: Sp<CasingStyle>, ) -> Self196 pub fn from_args_field(
197 field: &Field,
198 struct_casing: Sp<CasingStyle>,
199 env_casing: Sp<CasingStyle>,
200 ) -> Self {
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 res
248 }
249
new( name: Name, ident: Ident, ty: Option<Type>, casing: Sp<CasingStyle>, env_casing: Sp<CasingStyle>, kind: Sp<Kind>, ) -> Self250 fn new(
251 name: Name,
252 ident: Ident,
253 ty: Option<Type>,
254 casing: Sp<CasingStyle>,
255 env_casing: Sp<CasingStyle>,
256 kind: Sp<Kind>,
257 ) -> Self {
258 Self {
259 name,
260 ident,
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 kind,
277 }
278 }
279
push_method(&mut self, kind: AttrKind, name: Ident, arg: impl ToTokens)280 fn push_method(&mut self, kind: AttrKind, name: Ident, arg: impl ToTokens) {
281 if name == "id" {
282 match kind {
283 AttrKind::Command | AttrKind::Value => {
284 self.deprecations.push(Deprecation {
285 span: name.span(),
286 id: "id_is_only_for_arg",
287 version: "4.0.0",
288 description: format!(
289 "`#[{}(id)] was allowed by mistake, instead use `#[{}(name)]`",
290 kind.as_str(),
291 kind.as_str()
292 ),
293 });
294 }
295 AttrKind::Group | AttrKind::Arg | AttrKind::Clap | AttrKind::StructOpt => {}
296 }
297 self.name = Name::Assigned(quote!(#arg));
298 } else if name == "name" {
299 match kind {
300 AttrKind::Arg => {
301 self.deprecations.push(Deprecation {
302 span: name.span(),
303 id: "id_is_only_for_arg",
304 version: "4.0.0",
305 description: format!(
306 "`#[{}(name)] was allowed by mistake, instead use `#[{}(id)]` or `#[{}(value_name)]`",
307 kind.as_str(),
308 kind.as_str(),
309 kind.as_str()
310 ),
311 });
312 }
313 AttrKind::Group
314 | AttrKind::Command
315 | AttrKind::Value
316 | AttrKind::Clap
317 | AttrKind::StructOpt => {}
318 }
319 self.name = Name::Assigned(quote!(#arg));
320 } else if name == "value_parser" {
321 self.value_parser = Some(ValueParser::Explicit(Method::new(name, quote!(#arg))));
322 } else if name == "action" {
323 self.action = Some(Action::Explicit(Method::new(name, quote!(#arg))));
324 } else {
325 if name == "short" || name == "long" {
326 self.is_positional = false;
327 }
328 self.methods.push(Method::new(name, quote!(#arg)));
329 }
330 }
331
infer_kind(&mut self, attrs: &[ClapAttr])332 fn infer_kind(&mut self, attrs: &[ClapAttr]) {
333 for attr in attrs {
334 if let Some(AttrValue::Call(_)) = &attr.value {
335 continue;
336 }
337
338 let actual_attr_kind = *attr.kind.get();
339 let kind = match &attr.magic {
340 Some(MagicAttrName::FromGlobal) => {
341 if attr.value.is_some() {
342 let expr = attr.value_or_abort();
343 abort!(expr, "attribute `{}` does not accept a value", attr.name);
344 }
345 let ty = self
346 .kind()
347 .ty()
348 .cloned()
349 .unwrap_or_else(|| Sp::new(Ty::Other, self.kind.span()));
350 let kind = Sp::new(Kind::FromGlobal(ty), attr.name.clone().span());
351 Some(kind)
352 }
353 Some(MagicAttrName::Subcommand) if attr.value.is_none() => {
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::Subcommand(ty), attr.name.clone().span());
364 Some(kind)
365 }
366 Some(MagicAttrName::ExternalSubcommand) 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 kind = Sp::new(Kind::ExternalSubcommand, attr.name.clone().span());
372 Some(kind)
373 }
374 Some(MagicAttrName::Flatten) if attr.value.is_none() => {
375 if attr.value.is_some() {
376 let expr = attr.value_or_abort();
377 abort!(expr, "attribute `{}` does not accept a value", attr.name);
378 }
379 let ty = self
380 .kind()
381 .ty()
382 .cloned()
383 .unwrap_or_else(|| Sp::new(Ty::Other, self.kind.span()));
384 let kind = Sp::new(Kind::Flatten(ty), attr.name.clone().span());
385 Some(kind)
386 }
387 Some(MagicAttrName::Skip) if actual_attr_kind != AttrKind::Group => {
388 let expr = attr.value.clone();
389 let kind = Sp::new(
390 Kind::Skip(expr, self.kind.attr_kind()),
391 attr.name.clone().span(),
392 );
393 Some(kind)
394 }
395 _ => None,
396 };
397
398 if let Some(kind) = kind {
399 self.set_kind(kind);
400 }
401 }
402 }
403
push_attrs(&mut self, attrs: &[ClapAttr])404 fn push_attrs(&mut self, attrs: &[ClapAttr]) {
405 for attr in attrs {
406 let actual_attr_kind = *attr.kind.get();
407 let expected_attr_kind = self.kind.attr_kind();
408 match (actual_attr_kind, expected_attr_kind) {
409 (AttrKind::Clap, _) | (AttrKind::StructOpt, _) => {
410 self.deprecations.push(Deprecation::attribute(
411 "4.0.0",
412 actual_attr_kind,
413 expected_attr_kind,
414 attr.kind.span(),
415 ));
416 }
417
418 (AttrKind::Group, AttrKind::Command) => {}
419
420 _ if attr.kind != expected_attr_kind => {
421 abort!(
422 attr.kind.span(),
423 "Expected `{}` attribute instead of `{}`",
424 expected_attr_kind.as_str(),
425 actual_attr_kind.as_str()
426 );
427 }
428
429 _ => {}
430 }
431
432 if let Some(AttrValue::Call(tokens)) = &attr.value {
433 // Force raw mode with method call syntax
434 self.push_method(*attr.kind.get(), attr.name.clone(), quote!(#(#tokens),*));
435 continue;
436 }
437
438 match &attr.magic {
439 Some(MagicAttrName::Short) if attr.value.is_none() => {
440 assert_attr_kind(attr, &[AttrKind::Arg]);
441
442 self.push_method(
443 *attr.kind.get(),
444 attr.name.clone(),
445 self.name.clone().translate_char(*self.casing),
446 );
447 }
448
449 Some(MagicAttrName::Long) if attr.value.is_none() => {
450 assert_attr_kind(attr, &[AttrKind::Arg]);
451
452 self.push_method(*attr.kind.get(), attr.name.clone(), self.name.clone().translate(*self.casing));
453 }
454
455 Some(MagicAttrName::ValueParser) if attr.value.is_none() => {
456 assert_attr_kind(attr, &[AttrKind::Arg]);
457
458 self.deprecations.push(Deprecation {
459 span: attr.name.span(),
460 id: "bare_value_parser",
461 version: "4.0.0",
462 description: "`#[arg(value_parser)]` is now the default and is no longer needed`".to_owned(),
463 });
464 self.value_parser = Some(ValueParser::Implicit(attr.name.clone()));
465 }
466
467 Some(MagicAttrName::Action) if attr.value.is_none() => {
468 assert_attr_kind(attr, &[AttrKind::Arg]);
469
470 self.deprecations.push(Deprecation {
471 span: attr.name.span(),
472 id: "bare_action",
473 version: "4.0.0",
474 description: "`#[arg(action)]` is now the default and is no longer needed`".to_owned(),
475 });
476 self.action = Some(Action::Implicit(attr.name.clone()));
477 }
478
479 Some(MagicAttrName::Env) if attr.value.is_none() => {
480 assert_attr_kind(attr, &[AttrKind::Arg]);
481
482 self.push_method(
483 *attr.kind.get(),
484 attr.name.clone(),
485 self.name.clone().translate(*self.env_casing),
486 );
487 }
488
489 Some(MagicAttrName::ValueEnum) if attr.value.is_none() => {
490 assert_attr_kind(attr, &[AttrKind::Arg]);
491
492 self.is_enum = true
493 }
494
495 Some(MagicAttrName::VerbatimDocComment) if attr.value.is_none() => {
496 self.verbatim_doc_comment = true
497 }
498
499 Some(MagicAttrName::About) if attr.value.is_none() => {
500 assert_attr_kind(attr, &[AttrKind::Command]);
501
502 if let Some(method) =
503 Method::from_env(attr.name.clone(), "CARGO_PKG_DESCRIPTION")
504 {
505 self.methods.push(method);
506 }
507 }
508
509 Some(MagicAttrName::LongAbout) if attr.value.is_none() => {
510 assert_attr_kind(attr, &[AttrKind::Command]);
511
512 self.force_long_help = true;
513 }
514
515 Some(MagicAttrName::LongHelp) if attr.value.is_none() => {
516 assert_attr_kind(attr, &[AttrKind::Arg]);
517
518 self.force_long_help = true;
519 }
520
521 Some(MagicAttrName::Author) if attr.value.is_none() => {
522 assert_attr_kind(attr, &[AttrKind::Command]);
523
524 if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_AUTHORS") {
525 self.methods.push(method);
526 }
527 }
528
529 Some(MagicAttrName::Version) if attr.value.is_none() => {
530 assert_attr_kind(attr, &[AttrKind::Command]);
531
532 if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_VERSION") {
533 self.methods.push(method);
534 }
535 }
536
537 Some(MagicAttrName::DefaultValueT) => {
538 assert_attr_kind(attr, &[AttrKind::Arg]);
539
540 let ty = if let Some(ty) = self.ty.as_ref() {
541 ty
542 } else {
543 abort!(
544 attr.name.clone(),
545 "#[arg(default_value_t)] (without an argument) can be used \
546 only on field level";
547
548 note = "see \
549 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
550 };
551
552 let val = if let Some(expr) = &attr.value {
553 quote!(#expr)
554 } else {
555 quote!(<#ty as ::std::default::Default>::default())
556 };
557
558 let val = if attrs
559 .iter()
560 .any(|a| a.magic == Some(MagicAttrName::ValueEnum))
561 {
562 quote_spanned!(attr.name.clone().span()=> {
563 static DEFAULT_VALUE: clap::__macro_refs::once_cell::sync::Lazy<String> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
564 let val: #ty = #val;
565 clap::ValueEnum::to_possible_value(&val).unwrap().get_name().to_owned()
566 });
567 let s: &'static str = &*DEFAULT_VALUE;
568 s
569 })
570 } else {
571 quote_spanned!(attr.name.clone().span()=> {
572 static DEFAULT_VALUE: clap::__macro_refs::once_cell::sync::Lazy<String> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
573 let val: #ty = #val;
574 ::std::string::ToString::to_string(&val)
575 });
576 let s: &'static str = &*DEFAULT_VALUE;
577 s
578 })
579 };
580
581 let raw_ident = Ident::new("default_value", attr.name.clone().span());
582 self.methods.push(Method::new(raw_ident, val));
583 }
584
585 Some(MagicAttrName::DefaultValuesT) => {
586 assert_attr_kind(attr, &[AttrKind::Arg]);
587
588 let ty = if let Some(ty) = self.ty.as_ref() {
589 ty
590 } else {
591 abort!(
592 attr.name.clone(),
593 "#[arg(default_values_t)] (without an argument) can be used \
594 only on field level";
595
596 note = "see \
597 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
598 };
599 let expr = attr.value_or_abort();
600
601 let container_type = Ty::from_syn_ty(ty);
602 if *container_type != Ty::Vec {
603 abort!(
604 attr.name.clone(),
605 "#[arg(default_values_t)] can be used only on Vec types";
606
607 note = "see \
608 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
609 }
610 let inner_type = inner_type(ty);
611
612 // Use `Borrow<#inner_type>` so we accept `&Vec<#inner_type>` and
613 // `Vec<#inner_type>`.
614 let val = if attrs
615 .iter()
616 .any(|a| a.magic == Some(MagicAttrName::ValueEnum))
617 {
618 quote_spanned!(attr.name.clone().span()=> {
619 {
620 fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=String>
621 where
622 T: ::std::borrow::Borrow<#inner_type>
623 {
624 iterable
625 .into_iter()
626 .map(|val| {
627 clap::ValueEnum::to_possible_value(val.borrow()).unwrap().get_name().to_owned()
628 })
629 }
630
631 static DEFAULT_STRINGS: clap::__macro_refs::once_cell::sync::Lazy<Vec<::std::string::String>> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
632 iter_to_vals(#expr).collect()
633 });
634
635 static DEFAULT_VALUES: clap::__macro_refs::once_cell::sync::Lazy<Vec<&str>> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
636 DEFAULT_STRINGS.iter().map(::std::string::String::as_str).collect()
637 });
638 DEFAULT_VALUES.iter().copied()
639 }
640 })
641 } else {
642 quote_spanned!(attr.name.clone().span()=> {
643 {
644 fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=String>
645 where
646 T: ::std::borrow::Borrow<#inner_type>
647 {
648 iterable.into_iter().map(|val| val.borrow().to_string())
649 }
650
651 static DEFAULT_STRINGS: clap::__macro_refs::once_cell::sync::Lazy<Vec<::std::string::String>> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
652 iter_to_vals(#expr).collect()
653 });
654
655 static DEFAULT_VALUES: clap::__macro_refs::once_cell::sync::Lazy<Vec<&str>> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
656 DEFAULT_STRINGS.iter().map(::std::string::String::as_str).collect()
657 });
658 DEFAULT_VALUES.iter().copied()
659 }
660 })
661 };
662
663 self.methods.push(Method::new(
664 Ident::new("default_values", attr.name.clone().span()),
665 val,
666 ));
667 }
668
669 Some(MagicAttrName::DefaultValueOsT) => {
670 assert_attr_kind(attr, &[AttrKind::Arg]);
671
672 let ty = if let Some(ty) = self.ty.as_ref() {
673 ty
674 } else {
675 abort!(
676 attr.name.clone(),
677 "#[arg(default_value_os_t)] (without an argument) can be used \
678 only on field level";
679
680 note = "see \
681 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
682 };
683
684 let val = if let Some(expr) = &attr.value {
685 quote!(#expr)
686 } else {
687 quote!(<#ty as ::std::default::Default>::default())
688 };
689
690 let val = if attrs
691 .iter()
692 .any(|a| a.magic == Some(MagicAttrName::ValueEnum))
693 {
694 quote_spanned!(attr.name.clone().span()=> {
695 static DEFAULT_VALUE: clap::__macro_refs::once_cell::sync::Lazy<::std::ffi::OsString> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
696 let val: #ty = #val;
697 clap::ValueEnum::to_possible_value(&val).unwrap().get_name().to_owned()
698 });
699 let s: &'static ::std::ffi::OsStr = &*DEFAULT_VALUE;
700 s
701 })
702 } else {
703 quote_spanned!(attr.name.clone().span()=> {
704 static DEFAULT_VALUE: clap::__macro_refs::once_cell::sync::Lazy<::std::ffi::OsString> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
705 let val: #ty = #val;
706 ::std::ffi::OsString::from(val)
707 });
708 let s: &'static ::std::ffi::OsStr = &*DEFAULT_VALUE;
709 s
710 })
711 };
712
713 let raw_ident = Ident::new("default_value", attr.name.clone().span());
714 self.methods.push(Method::new(raw_ident, val));
715 }
716
717 Some(MagicAttrName::DefaultValuesOsT) => {
718 assert_attr_kind(attr, &[AttrKind::Arg]);
719
720 let ty = if let Some(ty) = self.ty.as_ref() {
721 ty
722 } else {
723 abort!(
724 attr.name.clone(),
725 "#[arg(default_values_os_t)] (without an argument) can be used \
726 only on field level";
727
728 note = "see \
729 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
730 };
731 let expr = attr.value_or_abort();
732
733 let container_type = Ty::from_syn_ty(ty);
734 if *container_type != Ty::Vec {
735 abort!(
736 attr.name.clone(),
737 "#[arg(default_values_os_t)] can be used only on Vec types";
738
739 note = "see \
740 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
741 }
742 let inner_type = inner_type(ty);
743
744 // Use `Borrow<#inner_type>` so we accept `&Vec<#inner_type>` and
745 // `Vec<#inner_type>`.
746 let val = if attrs
747 .iter()
748 .any(|a| a.magic == Some(MagicAttrName::ValueEnum))
749 {
750 quote_spanned!(attr.name.clone().span()=> {
751 {
752 fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=::std::ffi::OsString>
753 where
754 T: ::std::borrow::Borrow<#inner_type>
755 {
756 iterable
757 .into_iter()
758 .map(|val| {
759 clap::ValueEnum::to_possible_value(val.borrow()).unwrap().get_name().to_owned().into()
760 })
761 }
762
763 static DEFAULT_OS_STRINGS: clap::__macro_refs::once_cell::sync::Lazy<Vec<::std::ffi::OsString>> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
764 iter_to_vals(#expr).collect()
765 });
766
767 static DEFAULT_VALUES: clap::__macro_refs::once_cell::sync::Lazy<Vec<&::std::ffi::OsStr>> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
768 DEFAULT_OS_STRINGS.iter().map(::std::ffi::OsString::as_os_str).collect()
769 });
770 DEFAULT_VALUES.iter().copied()
771 }
772 })
773 } else {
774 quote_spanned!(attr.name.clone().span()=> {
775 {
776 fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=::std::ffi::OsString>
777 where
778 T: ::std::borrow::Borrow<#inner_type>
779 {
780 iterable.into_iter().map(|val| val.borrow().into())
781 }
782
783 static DEFAULT_OS_STRINGS: clap::__macro_refs::once_cell::sync::Lazy<Vec<::std::ffi::OsString>> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
784 iter_to_vals(#expr).collect()
785 });
786
787 static DEFAULT_VALUES: clap::__macro_refs::once_cell::sync::Lazy<Vec<&::std::ffi::OsStr>> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
788 DEFAULT_OS_STRINGS.iter().map(::std::ffi::OsString::as_os_str).collect()
789 });
790 DEFAULT_VALUES.iter().copied()
791 }
792 })
793 };
794
795 self.methods.push(Method::new(
796 Ident::new("default_values", attr.name.clone().span()),
797 val,
798 ));
799 }
800
801 Some(MagicAttrName::NextDisplayOrder) => {
802 assert_attr_kind(attr, &[AttrKind::Command]);
803
804 let expr = attr.value_or_abort();
805 self.next_display_order = Some(Method::new(attr.name.clone(), quote!(#expr)));
806 }
807
808 Some(MagicAttrName::NextHelpHeading) => {
809 assert_attr_kind(attr, &[AttrKind::Command]);
810
811 let expr = attr.value_or_abort();
812 self.next_help_heading = Some(Method::new(attr.name.clone(), quote!(#expr)));
813 }
814
815 Some(MagicAttrName::RenameAll) => {
816 let lit = attr.lit_str_or_abort();
817 self.casing = CasingStyle::from_lit(lit);
818 }
819
820 Some(MagicAttrName::RenameAllEnv) => {
821 assert_attr_kind(attr, &[AttrKind::Command, AttrKind::Arg]);
822
823 let lit = attr.lit_str_or_abort();
824 self.env_casing = CasingStyle::from_lit(lit);
825 }
826
827 Some(MagicAttrName::Skip) if actual_attr_kind == AttrKind::Group => {
828 self.skip_group = true;
829 }
830
831 None
832 // Magic only for the default, otherwise just forward to the builder
833 | Some(MagicAttrName::Short)
834 | Some(MagicAttrName::Long)
835 | Some(MagicAttrName::Env)
836 | Some(MagicAttrName::About)
837 | Some(MagicAttrName::LongAbout)
838 | Some(MagicAttrName::LongHelp)
839 | Some(MagicAttrName::Author)
840 | Some(MagicAttrName::Version)
841 => {
842 let expr = attr.value_or_abort();
843 self.push_method(*attr.kind.get(), attr.name.clone(), expr);
844 }
845
846 // Magic only for the default, otherwise just forward to the builder
847 Some(MagicAttrName::ValueParser) | Some(MagicAttrName::Action) => {
848 let expr = attr.value_or_abort();
849 self.push_method(*attr.kind.get(), attr.name.clone(), expr);
850 }
851
852 // Directives that never receive a value
853 Some(MagicAttrName::ValueEnum)
854 | Some(MagicAttrName::VerbatimDocComment) => {
855 let expr = attr.value_or_abort();
856 abort!(expr, "attribute `{}` does not accept a value", attr.name);
857 }
858
859 // Kinds
860 Some(MagicAttrName::FromGlobal)
861 | Some(MagicAttrName::Subcommand)
862 | Some(MagicAttrName::ExternalSubcommand)
863 | Some(MagicAttrName::Flatten)
864 | Some(MagicAttrName::Skip) => {
865 }
866 }
867 }
868
869 if self.has_explicit_methods() {
870 if let Kind::Skip(_, attr) = &*self.kind {
871 abort!(
872 self.methods[0].name.span(),
873 "`{}` cannot be used with `#[{}(skip)]",
874 self.methods[0].name,
875 attr.as_str(),
876 );
877 }
878 if let Kind::FromGlobal(_) = &*self.kind {
879 abort!(
880 self.methods[0].name.span(),
881 "`{}` cannot be used with `#[arg(from_global)]",
882 self.methods[0].name,
883 );
884 }
885 }
886 }
887
push_doc_comment(&mut self, attrs: &[Attribute], short_name: &str, long_name: Option<&str>)888 fn push_doc_comment(&mut self, attrs: &[Attribute], short_name: &str, long_name: Option<&str>) {
889 let lines = extract_doc_comment(attrs);
890
891 if !lines.is_empty() {
892 let (short_help, long_help) =
893 format_doc_comment(&lines, !self.verbatim_doc_comment, self.force_long_help);
894 let short_name = format_ident!("{}", short_name);
895 let short = Method::new(
896 short_name,
897 short_help
898 .map(|h| quote!(#h))
899 .unwrap_or_else(|| quote!(None)),
900 );
901 self.doc_comment.push(short);
902 if let Some(long_name) = long_name {
903 let long_name = format_ident!("{}", long_name);
904 let long = Method::new(
905 long_name,
906 long_help
907 .map(|h| quote!(#h))
908 .unwrap_or_else(|| quote!(None)),
909 );
910 self.doc_comment.push(long);
911 }
912 }
913 }
914
set_kind(&mut self, kind: Sp<Kind>)915 fn set_kind(&mut self, kind: Sp<Kind>) {
916 match (self.kind.get(), kind.get()) {
917 (Kind::Arg(_), Kind::FromGlobal(_))
918 | (Kind::Arg(_), Kind::Subcommand(_))
919 | (Kind::Arg(_), Kind::Flatten(_))
920 | (Kind::Arg(_), Kind::Skip(_, _))
921 | (Kind::Command(_), Kind::Subcommand(_))
922 | (Kind::Command(_), Kind::Flatten(_))
923 | (Kind::Command(_), Kind::Skip(_, _))
924 | (Kind::Command(_), Kind::ExternalSubcommand)
925 | (Kind::Value, Kind::Skip(_, _)) => {
926 self.kind = kind;
927 }
928
929 (_, _) => {
930 let old = self.kind.name();
931 let new = kind.name();
932 abort!(kind.span(), "`{}` cannot be used with `{}`", new, old);
933 }
934 }
935 }
936
find_default_method(&self) -> Option<&Method>937 pub fn find_default_method(&self) -> Option<&Method> {
938 self.methods
939 .iter()
940 .find(|m| m.name == "default_value" || m.name == "default_value_os")
941 }
942
943 /// generate methods from attributes on top of struct or enum
initial_top_level_methods(&self) -> TokenStream944 pub fn initial_top_level_methods(&self) -> TokenStream {
945 let next_display_order = self.next_display_order.as_ref().into_iter();
946 let next_help_heading = self.next_help_heading.as_ref().into_iter();
947 quote!(
948 #(#next_display_order)*
949 #(#next_help_heading)*
950 )
951 }
952
final_top_level_methods(&self) -> TokenStream953 pub fn final_top_level_methods(&self) -> TokenStream {
954 let methods = &self.methods;
955 let doc_comment = &self.doc_comment;
956
957 quote!( #(#doc_comment)* #(#methods)*)
958 }
959
960 /// generate methods on top of a field
field_methods(&self) -> proc_macro2::TokenStream961 pub fn field_methods(&self) -> proc_macro2::TokenStream {
962 let methods = &self.methods;
963 let doc_comment = &self.doc_comment;
964 quote!( #(#doc_comment)* #(#methods)* )
965 }
966
deprecations(&self) -> proc_macro2::TokenStream967 pub fn deprecations(&self) -> proc_macro2::TokenStream {
968 let deprecations = &self.deprecations;
969 quote!( #(#deprecations)* )
970 }
971
next_display_order(&self) -> TokenStream972 pub fn next_display_order(&self) -> TokenStream {
973 let next_display_order = self.next_display_order.as_ref().into_iter();
974 quote!( #(#next_display_order)* )
975 }
976
next_help_heading(&self) -> TokenStream977 pub fn next_help_heading(&self) -> TokenStream {
978 let next_help_heading = self.next_help_heading.as_ref().into_iter();
979 quote!( #(#next_help_heading)* )
980 }
981
ident(&self) -> &Ident982 pub fn ident(&self) -> &Ident {
983 &self.ident
984 }
985
id(&self) -> TokenStream986 pub fn id(&self) -> TokenStream {
987 self.name.clone().raw()
988 }
989
cased_name(&self) -> TokenStream990 pub fn cased_name(&self) -> TokenStream {
991 self.name.clone().translate(*self.casing)
992 }
993
value_name(&self) -> TokenStream994 pub fn value_name(&self) -> TokenStream {
995 self.name.clone().translate(CasingStyle::ScreamingSnake)
996 }
997
value_parser(&self, field_type: &Type) -> Method998 pub fn value_parser(&self, field_type: &Type) -> Method {
999 self.value_parser
1000 .clone()
1001 .map(|p| {
1002 let inner_type = inner_type(field_type);
1003 p.resolve(inner_type)
1004 })
1005 .unwrap_or_else(|| {
1006 let inner_type = inner_type(field_type);
1007 if let Some(action) = self.action.as_ref() {
1008 let span = action.span();
1009 default_value_parser(inner_type, span)
1010 } else {
1011 let span = self
1012 .action
1013 .as_ref()
1014 .map(|a| a.span())
1015 .unwrap_or_else(|| self.kind.span());
1016 default_value_parser(inner_type, span)
1017 }
1018 })
1019 }
1020
action(&self, field_type: &Type) -> Method1021 pub fn action(&self, field_type: &Type) -> Method {
1022 self.action
1023 .clone()
1024 .map(|p| p.resolve(field_type))
1025 .unwrap_or_else(|| {
1026 if let Some(value_parser) = self.value_parser.as_ref() {
1027 let span = value_parser.span();
1028 default_action(field_type, span)
1029 } else {
1030 let span = self
1031 .value_parser
1032 .as_ref()
1033 .map(|a| a.span())
1034 .unwrap_or_else(|| self.kind.span());
1035 default_action(field_type, span)
1036 }
1037 })
1038 }
1039
kind(&self) -> Sp<Kind>1040 pub fn kind(&self) -> Sp<Kind> {
1041 self.kind.clone()
1042 }
1043
is_positional(&self) -> bool1044 pub fn is_positional(&self) -> bool {
1045 self.is_positional
1046 }
1047
casing(&self) -> Sp<CasingStyle>1048 pub fn casing(&self) -> Sp<CasingStyle> {
1049 self.casing
1050 }
1051
env_casing(&self) -> Sp<CasingStyle>1052 pub fn env_casing(&self) -> Sp<CasingStyle> {
1053 self.env_casing
1054 }
1055
has_explicit_methods(&self) -> bool1056 pub fn has_explicit_methods(&self) -> bool {
1057 self.methods
1058 .iter()
1059 .any(|m| m.name != "help" && m.name != "long_help")
1060 }
1061
skip_group(&self) -> bool1062 pub fn skip_group(&self) -> bool {
1063 self.skip_group
1064 }
1065 }
1066
1067 #[derive(Clone)]
1068 enum ValueParser {
1069 Explicit(Method),
1070 Implicit(Ident),
1071 }
1072
1073 impl ValueParser {
resolve(self, _inner_type: &Type) -> Method1074 fn resolve(self, _inner_type: &Type) -> Method {
1075 match self {
1076 Self::Explicit(method) => method,
1077 Self::Implicit(ident) => default_value_parser(_inner_type, ident.span()),
1078 }
1079 }
1080
span(&self) -> Span1081 fn span(&self) -> Span {
1082 match self {
1083 Self::Explicit(method) => method.name.span(),
1084 Self::Implicit(ident) => ident.span(),
1085 }
1086 }
1087 }
1088
default_value_parser(inner_type: &Type, span: Span) -> Method1089 fn default_value_parser(inner_type: &Type, span: Span) -> Method {
1090 let func = Ident::new("value_parser", span);
1091 Method::new(
1092 func,
1093 quote_spanned! { span=>
1094 clap::value_parser!(#inner_type)
1095 },
1096 )
1097 }
1098
1099 #[derive(Clone)]
1100 pub enum Action {
1101 Explicit(Method),
1102 Implicit(Ident),
1103 }
1104
1105 impl Action {
resolve(self, _field_type: &Type) -> Method1106 pub fn resolve(self, _field_type: &Type) -> Method {
1107 match self {
1108 Self::Explicit(method) => method,
1109 Self::Implicit(ident) => default_action(_field_type, ident.span()),
1110 }
1111 }
1112
span(&self) -> Span1113 pub fn span(&self) -> Span {
1114 match self {
1115 Self::Explicit(method) => method.name.span(),
1116 Self::Implicit(ident) => ident.span(),
1117 }
1118 }
1119 }
1120
default_action(field_type: &Type, span: Span) -> Method1121 fn default_action(field_type: &Type, span: Span) -> Method {
1122 let ty = Ty::from_syn_ty(field_type);
1123 let args = match *ty {
1124 Ty::Vec | Ty::OptionVec | Ty::VecVec | Ty::OptionVecVec => {
1125 quote_spanned! { span=>
1126 clap::ArgAction::Append
1127 }
1128 }
1129 Ty::Option | Ty::OptionOption => {
1130 quote_spanned! { span=>
1131 clap::ArgAction::Set
1132 }
1133 }
1134 _ => {
1135 if is_simple_ty(field_type, "bool") {
1136 quote_spanned! { span=>
1137 clap::ArgAction::SetTrue
1138 }
1139 } else {
1140 quote_spanned! { span=>
1141 clap::ArgAction::Set
1142 }
1143 }
1144 }
1145 };
1146
1147 let func = Ident::new("action", span);
1148 Method::new(func, args)
1149 }
1150
1151 #[allow(clippy::large_enum_variant)]
1152 #[derive(Clone)]
1153 pub enum Kind {
1154 Arg(Sp<Ty>),
1155 Command(Sp<Ty>),
1156 Value,
1157 FromGlobal(Sp<Ty>),
1158 Subcommand(Sp<Ty>),
1159 Flatten(Sp<Ty>),
1160 Skip(Option<AttrValue>, AttrKind),
1161 ExternalSubcommand,
1162 }
1163
1164 impl Kind {
name(&self) -> &'static str1165 pub fn name(&self) -> &'static str {
1166 match self {
1167 Self::Arg(_) => "arg",
1168 Self::Command(_) => "command",
1169 Self::Value => "value",
1170 Self::FromGlobal(_) => "from_global",
1171 Self::Subcommand(_) => "subcommand",
1172 Self::Flatten(_) => "flatten",
1173 Self::Skip(_, _) => "skip",
1174 Self::ExternalSubcommand => "external_subcommand",
1175 }
1176 }
1177
attr_kind(&self) -> AttrKind1178 pub fn attr_kind(&self) -> AttrKind {
1179 match self {
1180 Self::Arg(_) => AttrKind::Arg,
1181 Self::Command(_) => AttrKind::Command,
1182 Self::Value => AttrKind::Value,
1183 Self::FromGlobal(_) => AttrKind::Arg,
1184 Self::Subcommand(_) => AttrKind::Command,
1185 Self::Flatten(_) => AttrKind::Command,
1186 Self::Skip(_, kind) => *kind,
1187 Self::ExternalSubcommand => AttrKind::Command,
1188 }
1189 }
1190
ty(&self) -> Option<&Sp<Ty>>1191 pub fn ty(&self) -> Option<&Sp<Ty>> {
1192 match self {
1193 Self::Arg(ty)
1194 | Self::Command(ty)
1195 | Self::Flatten(ty)
1196 | Self::FromGlobal(ty)
1197 | Self::Subcommand(ty) => Some(ty),
1198 Self::Value | Self::Skip(_, _) | Self::ExternalSubcommand => None,
1199 }
1200 }
1201 }
1202
1203 #[derive(Clone)]
1204 pub struct Method {
1205 name: Ident,
1206 args: TokenStream,
1207 }
1208
1209 impl Method {
new(name: Ident, args: TokenStream) -> Self1210 pub fn new(name: Ident, args: TokenStream) -> Self {
1211 Method { name, args }
1212 }
1213
from_env(ident: Ident, env_var: &str) -> Option<Self>1214 fn from_env(ident: Ident, env_var: &str) -> Option<Self> {
1215 let mut lit = match env::var(env_var) {
1216 Ok(val) => {
1217 if val.is_empty() {
1218 return None;
1219 }
1220 LitStr::new(&val, ident.span())
1221 }
1222 Err(_) => {
1223 abort!(ident,
1224 "cannot derive `{}` from Cargo.toml", ident;
1225 note = "`{}` environment variable is not set", env_var;
1226 help = "use `{} = \"...\"` to set {} manually", ident, ident;
1227 );
1228 }
1229 };
1230
1231 if ident == "author" {
1232 let edited = process_author_str(&lit.value());
1233 lit = LitStr::new(&edited, lit.span());
1234 }
1235
1236 Some(Method::new(ident, quote!(#lit)))
1237 }
1238
args(&self) -> &TokenStream1239 pub(crate) fn args(&self) -> &TokenStream {
1240 &self.args
1241 }
1242 }
1243
1244 impl ToTokens for Method {
to_tokens(&self, ts: &mut proc_macro2::TokenStream)1245 fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
1246 let Method { ref name, ref args } = self;
1247
1248 let tokens = quote!( .#name(#args) );
1249
1250 tokens.to_tokens(ts);
1251 }
1252 }
1253
1254 #[derive(Clone)]
1255 pub struct Deprecation {
1256 pub span: Span,
1257 pub id: &'static str,
1258 pub version: &'static str,
1259 pub description: String,
1260 }
1261
1262 impl Deprecation {
attribute(version: &'static str, old: AttrKind, new: AttrKind, span: Span) -> Self1263 fn attribute(version: &'static str, old: AttrKind, new: AttrKind, span: Span) -> Self {
1264 Self {
1265 span,
1266 id: "old_attribute",
1267 version,
1268 description: format!(
1269 "Attribute `#[{}(...)]` has been deprecated in favor of `#[{}(...)]`",
1270 old.as_str(),
1271 new.as_str()
1272 ),
1273 }
1274 }
1275 }
1276
1277 impl ToTokens for Deprecation {
to_tokens(&self, ts: &mut proc_macro2::TokenStream)1278 fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
1279 let tokens = if cfg!(feature = "deprecated") {
1280 let Deprecation {
1281 span,
1282 id,
1283 version,
1284 description,
1285 } = self;
1286 let span = *span;
1287 let id = Ident::new(id, span);
1288
1289 quote_spanned!(span=> {
1290 #[deprecated(since = #version, note = #description)]
1291 fn #id() {}
1292 #id();
1293 })
1294 } else {
1295 quote!()
1296 };
1297
1298 tokens.to_tokens(ts);
1299 }
1300 }
1301
assert_attr_kind(attr: &ClapAttr, possible_kind: &[AttrKind])1302 fn assert_attr_kind(attr: &ClapAttr, possible_kind: &[AttrKind]) {
1303 if *attr.kind.get() == AttrKind::Clap || *attr.kind.get() == AttrKind::StructOpt {
1304 // deprecated
1305 } else if !possible_kind.contains(attr.kind.get()) {
1306 let options = possible_kind
1307 .iter()
1308 .map(|k| format!("`#[{}({})]`", k.as_str(), attr.name))
1309 .collect::<Vec<_>>();
1310 abort!(
1311 attr.name,
1312 "Unknown `#[{}({})]` attribute ({} exists)",
1313 attr.kind.as_str(),
1314 attr.name,
1315 options.join(", ")
1316 );
1317 }
1318 }
1319
1320 /// replace all `:` with `, ` when not inside the `<>`
1321 ///
1322 /// `"author1:author2:author3" => "author1, author2, author3"`
1323 /// `"author1 <http://website1.com>:author2" => "author1 <http://website1.com>, author2"
process_author_str(author: &str) -> String1324 fn process_author_str(author: &str) -> String {
1325 let mut res = String::with_capacity(author.len());
1326 let mut inside_angle_braces = 0usize;
1327
1328 for ch in author.chars() {
1329 if inside_angle_braces > 0 && ch == '>' {
1330 inside_angle_braces -= 1;
1331 res.push(ch);
1332 } else if ch == '<' {
1333 inside_angle_braces += 1;
1334 res.push(ch);
1335 } else if inside_angle_braces == 0 && ch == ':' {
1336 res.push_str(", ");
1337 } else {
1338 res.push(ch);
1339 }
1340 }
1341
1342 res
1343 }
1344
1345 /// Defines the casing for the attributes long representation.
1346 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
1347 pub enum CasingStyle {
1348 /// Indicate word boundaries with uppercase letter, excluding the first word.
1349 Camel,
1350 /// Keep all letters lowercase and indicate word boundaries with hyphens.
1351 Kebab,
1352 /// Indicate word boundaries with uppercase letter, including the first word.
1353 Pascal,
1354 /// Keep all letters uppercase and indicate word boundaries with underscores.
1355 ScreamingSnake,
1356 /// Keep all letters lowercase and indicate word boundaries with underscores.
1357 Snake,
1358 /// Keep all letters lowercase and remove word boundaries.
1359 Lower,
1360 /// Keep all letters uppercase and remove word boundaries.
1361 Upper,
1362 /// Use the original attribute name defined in the code.
1363 Verbatim,
1364 }
1365
1366 impl CasingStyle {
from_lit(name: &LitStr) -> Sp<Self>1367 fn from_lit(name: &LitStr) -> Sp<Self> {
1368 use self::CasingStyle::*;
1369
1370 let normalized = name.value().to_upper_camel_case().to_lowercase();
1371 let cs = |kind| Sp::new(kind, name.span());
1372
1373 match normalized.as_ref() {
1374 "camel" | "camelcase" => cs(Camel),
1375 "kebab" | "kebabcase" => cs(Kebab),
1376 "pascal" | "pascalcase" => cs(Pascal),
1377 "screamingsnake" | "screamingsnakecase" => cs(ScreamingSnake),
1378 "snake" | "snakecase" => cs(Snake),
1379 "lower" | "lowercase" => cs(Lower),
1380 "upper" | "uppercase" => cs(Upper),
1381 "verbatim" | "verbatimcase" => cs(Verbatim),
1382 s => abort!(name, "unsupported casing: `{}`", s),
1383 }
1384 }
1385 }
1386
1387 #[derive(Clone)]
1388 pub enum Name {
1389 Derived(Ident),
1390 Assigned(TokenStream),
1391 }
1392
1393 impl Name {
raw(self) -> TokenStream1394 pub fn raw(self) -> TokenStream {
1395 match self {
1396 Name::Assigned(tokens) => tokens,
1397 Name::Derived(ident) => {
1398 let s = ident.unraw().to_string();
1399 quote_spanned!(ident.span()=> #s)
1400 }
1401 }
1402 }
1403
translate(self, style: CasingStyle) -> TokenStream1404 pub fn translate(self, style: CasingStyle) -> TokenStream {
1405 use CasingStyle::*;
1406
1407 match self {
1408 Name::Assigned(tokens) => tokens,
1409 Name::Derived(ident) => {
1410 let s = ident.unraw().to_string();
1411 let s = match style {
1412 Pascal => s.to_upper_camel_case(),
1413 Kebab => s.to_kebab_case(),
1414 Camel => s.to_lower_camel_case(),
1415 ScreamingSnake => s.to_shouty_snake_case(),
1416 Snake => s.to_snake_case(),
1417 Lower => s.to_snake_case().replace('_', ""),
1418 Upper => s.to_shouty_snake_case().replace('_', ""),
1419 Verbatim => s,
1420 };
1421 quote_spanned!(ident.span()=> #s)
1422 }
1423 }
1424 }
1425
translate_char(self, style: CasingStyle) -> TokenStream1426 pub fn translate_char(self, style: CasingStyle) -> TokenStream {
1427 use CasingStyle::*;
1428
1429 match self {
1430 Name::Assigned(tokens) => quote!( (#tokens).chars().next().unwrap() ),
1431 Name::Derived(ident) => {
1432 let s = ident.unraw().to_string();
1433 let s = match style {
1434 Pascal => s.to_upper_camel_case(),
1435 Kebab => s.to_kebab_case(),
1436 Camel => s.to_lower_camel_case(),
1437 ScreamingSnake => s.to_shouty_snake_case(),
1438 Snake => s.to_snake_case(),
1439 Lower => s.to_snake_case(),
1440 Upper => s.to_shouty_snake_case(),
1441 Verbatim => s,
1442 };
1443
1444 let s = s.chars().next().unwrap();
1445 quote_spanned!(ident.span()=> #s)
1446 }
1447 }
1448 }
1449 }
1450