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