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 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
55 impl Item {
from_args_struct(input: &DeriveInput, name: Name) -> Result<Self, syn::Error>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
from_subcommand_enum(input: &DeriveInput, name: Name) -> Result<Self, syn::Error>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
from_value_enum(input: &DeriveInput, name: Name) -> Result<Self, syn::Error>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
from_subcommand_variant( variant: &Variant, struct_casing: Sp<CasingStyle>, env_casing: Sp<CasingStyle>, ) -> Result<Self, syn::Error>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
from_value_enum_variant( variant: &Variant, argument_casing: Sp<CasingStyle>, env_casing: Sp<CasingStyle>, ) -> Result<Self, syn::Error>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
from_args_field( field: &Field, struct_casing: Sp<CasingStyle>, env_casing: Sp<CasingStyle>, ) -> Result<Self, syn::Error>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
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 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
push_method(&mut self, kind: AttrKind, name: Ident, arg: impl ToTokens)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
push_method_(&mut self, kind: AttrKind, name: Ident, arg: TokenStream)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
infer_kind(&mut self, attrs: &[ClapAttr]) -> Result<(), syn::Error>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
push_attrs(&mut self, attrs: &[ClapAttr]) -> Result<(), syn::Error>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: clap::__derive_refs::once_cell::sync::Lazy<String> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
579 let val: #ty = #val;
580 clap::ValueEnum::to_possible_value(&val).unwrap().get_name().to_owned()
581 });
582 let s: &'static str = &*DEFAULT_VALUE;
583 s
584 })
585 } else {
586 quote_spanned!(attr.name.clone().span()=> {
587 static DEFAULT_VALUE: clap::__derive_refs::once_cell::sync::Lazy<String> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
588 let val: #ty = #val;
589 ::std::string::ToString::to_string(&val)
590 });
591 let s: &'static str = &*DEFAULT_VALUE;
592 s
593 })
594 };
595
596 let raw_ident = Ident::new("default_value", attr.name.clone().span());
597 self.methods.push(Method::new(raw_ident, val));
598 }
599
600 Some(MagicAttrName::DefaultValuesT) => {
601 assert_attr_kind(attr, &[AttrKind::Arg])?;
602
603 let ty = if let Some(ty) = self.ty.as_ref() {
604 ty
605 } else {
606 abort!(
607 attr.name.clone(),
608 "#[arg(default_values_t)] (without an argument) can be used \
609 only on field level\n\n= note: {note}\n\n",
610
611 note = "see \
612 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
613 };
614 let expr = attr.value_or_abort()?;
615
616 let container_type = Ty::from_syn_ty(ty);
617 if *container_type != Ty::Vec {
618 abort!(
619 attr.name.clone(),
620 "#[arg(default_values_t)] can be used only on Vec types\n\n= note: {note}\n\n",
621
622 note = "see \
623 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
624 }
625 let inner_type = inner_type(ty);
626
627 // Use `Borrow<#inner_type>` so we accept `&Vec<#inner_type>` and
628 // `Vec<#inner_type>`.
629 let val = if attrs
630 .iter()
631 .any(|a| a.magic == Some(MagicAttrName::ValueEnum))
632 {
633 quote_spanned!(attr.name.clone().span()=> {
634 {
635 fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=String>
636 where
637 T: ::std::borrow::Borrow<#inner_type>
638 {
639 iterable
640 .into_iter()
641 .map(|val| {
642 clap::ValueEnum::to_possible_value(val.borrow()).unwrap().get_name().to_owned()
643 })
644 }
645
646 static DEFAULT_STRINGS: clap::__derive_refs::once_cell::sync::Lazy<Vec<::std::string::String>> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
647 iter_to_vals(#expr).collect()
648 });
649
650 static DEFAULT_VALUES: clap::__derive_refs::once_cell::sync::Lazy<Vec<&str>> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
651 DEFAULT_STRINGS.iter().map(::std::string::String::as_str).collect()
652 });
653 DEFAULT_VALUES.iter().copied()
654 }
655 })
656 } else {
657 quote_spanned!(attr.name.clone().span()=> {
658 {
659 fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=String>
660 where
661 T: ::std::borrow::Borrow<#inner_type>
662 {
663 iterable.into_iter().map(|val| val.borrow().to_string())
664 }
665
666 static DEFAULT_STRINGS: clap::__derive_refs::once_cell::sync::Lazy<Vec<::std::string::String>> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
667 iter_to_vals(#expr).collect()
668 });
669
670 static DEFAULT_VALUES: clap::__derive_refs::once_cell::sync::Lazy<Vec<&str>> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
671 DEFAULT_STRINGS.iter().map(::std::string::String::as_str).collect()
672 });
673 DEFAULT_VALUES.iter().copied()
674 }
675 })
676 };
677
678 self.methods.push(Method::new(
679 Ident::new("default_values", attr.name.clone().span()),
680 val,
681 ));
682 }
683
684 Some(MagicAttrName::DefaultValueOsT) => {
685 assert_attr_kind(attr, &[AttrKind::Arg])?;
686
687 let ty = if let Some(ty) = self.ty.as_ref() {
688 ty
689 } else {
690 abort!(
691 attr.name.clone(),
692 "#[arg(default_value_os_t)] (without an argument) can be used \
693 only on field level\n\n= note: {note}\n\n",
694
695 note = "see \
696 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
697 };
698
699 let val = if let Some(expr) = &attr.value {
700 quote!(#expr)
701 } else {
702 quote!(<#ty as ::std::default::Default>::default())
703 };
704
705 let val = if attrs
706 .iter()
707 .any(|a| a.magic == Some(MagicAttrName::ValueEnum))
708 {
709 quote_spanned!(attr.name.clone().span()=> {
710 static DEFAULT_VALUE: clap::__derive_refs::once_cell::sync::Lazy<::std::ffi::OsString> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
711 let val: #ty = #val;
712 clap::ValueEnum::to_possible_value(&val).unwrap().get_name().to_owned()
713 });
714 let s: &'static ::std::ffi::OsStr = &*DEFAULT_VALUE;
715 s
716 })
717 } else {
718 quote_spanned!(attr.name.clone().span()=> {
719 static DEFAULT_VALUE: clap::__derive_refs::once_cell::sync::Lazy<::std::ffi::OsString> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
720 let val: #ty = #val;
721 ::std::ffi::OsString::from(val)
722 });
723 let s: &'static ::std::ffi::OsStr = &*DEFAULT_VALUE;
724 s
725 })
726 };
727
728 let raw_ident = Ident::new("default_value", attr.name.clone().span());
729 self.methods.push(Method::new(raw_ident, val));
730 }
731
732 Some(MagicAttrName::DefaultValuesOsT) => {
733 assert_attr_kind(attr, &[AttrKind::Arg])?;
734
735 let ty = if let Some(ty) = self.ty.as_ref() {
736 ty
737 } else {
738 abort!(
739 attr.name.clone(),
740 "#[arg(default_values_os_t)] (without an argument) can be used \
741 only on field level\n\n= note: {note}\n\n",
742
743 note = "see \
744 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
745 };
746 let expr = attr.value_or_abort()?;
747
748 let container_type = Ty::from_syn_ty(ty);
749 if *container_type != Ty::Vec {
750 abort!(
751 attr.name.clone(),
752 "#[arg(default_values_os_t)] can be used only on Vec types\n\n= note: {note}\n\n",
753
754 note = "see \
755 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
756 }
757 let inner_type = inner_type(ty);
758
759 // Use `Borrow<#inner_type>` so we accept `&Vec<#inner_type>` and
760 // `Vec<#inner_type>`.
761 let val = if attrs
762 .iter()
763 .any(|a| a.magic == Some(MagicAttrName::ValueEnum))
764 {
765 quote_spanned!(attr.name.clone().span()=> {
766 {
767 fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=::std::ffi::OsString>
768 where
769 T: ::std::borrow::Borrow<#inner_type>
770 {
771 iterable
772 .into_iter()
773 .map(|val| {
774 clap::ValueEnum::to_possible_value(val.borrow()).unwrap().get_name().to_owned().into()
775 })
776 }
777
778 static DEFAULT_OS_STRINGS: clap::__derive_refs::once_cell::sync::Lazy<Vec<::std::ffi::OsString>> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
779 iter_to_vals(#expr).collect()
780 });
781
782 static DEFAULT_VALUES: clap::__derive_refs::once_cell::sync::Lazy<Vec<&::std::ffi::OsStr>> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
783 DEFAULT_OS_STRINGS.iter().map(::std::ffi::OsString::as_os_str).collect()
784 });
785 DEFAULT_VALUES.iter().copied()
786 }
787 })
788 } else {
789 quote_spanned!(attr.name.clone().span()=> {
790 {
791 fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=::std::ffi::OsString>
792 where
793 T: ::std::borrow::Borrow<#inner_type>
794 {
795 iterable.into_iter().map(|val| val.borrow().into())
796 }
797
798 static DEFAULT_OS_STRINGS: clap::__derive_refs::once_cell::sync::Lazy<Vec<::std::ffi::OsString>> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
799 iter_to_vals(#expr).collect()
800 });
801
802 static DEFAULT_VALUES: clap::__derive_refs::once_cell::sync::Lazy<Vec<&::std::ffi::OsStr>> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
803 DEFAULT_OS_STRINGS.iter().map(::std::ffi::OsString::as_os_str).collect()
804 });
805 DEFAULT_VALUES.iter().copied()
806 }
807 })
808 };
809
810 self.methods.push(Method::new(
811 Ident::new("default_values", attr.name.clone().span()),
812 val,
813 ));
814 }
815
816 Some(MagicAttrName::NextDisplayOrder) => {
817 assert_attr_kind(attr, &[AttrKind::Command])?;
818
819 let expr = attr.value_or_abort()?;
820 self.next_display_order = Some(Method::new(attr.name.clone(), quote!(#expr)));
821 }
822
823 Some(MagicAttrName::NextHelpHeading) => {
824 assert_attr_kind(attr, &[AttrKind::Command])?;
825
826 let expr = attr.value_or_abort()?;
827 self.next_help_heading = Some(Method::new(attr.name.clone(), quote!(#expr)));
828 }
829
830 Some(MagicAttrName::RenameAll) => {
831 let lit = attr.lit_str_or_abort()?;
832 self.casing = CasingStyle::from_lit(lit)?;
833 }
834
835 Some(MagicAttrName::RenameAllEnv) => {
836 assert_attr_kind(attr, &[AttrKind::Command, AttrKind::Arg])?;
837
838 let lit = attr.lit_str_or_abort()?;
839 self.env_casing = CasingStyle::from_lit(lit)?;
840 }
841
842 Some(MagicAttrName::Skip) if actual_attr_kind == AttrKind::Group => {
843 self.skip_group = true;
844 }
845
846 None
847 // Magic only for the default, otherwise just forward to the builder
848 | Some(MagicAttrName::Short)
849 | Some(MagicAttrName::Long)
850 | Some(MagicAttrName::Env)
851 | Some(MagicAttrName::About)
852 | Some(MagicAttrName::LongAbout)
853 | Some(MagicAttrName::LongHelp)
854 | Some(MagicAttrName::Author)
855 | Some(MagicAttrName::Version)
856 => {
857 let expr = attr.value_or_abort()?;
858 self.push_method(*attr.kind.get(), attr.name.clone(), expr);
859 }
860
861 // Magic only for the default, otherwise just forward to the builder
862 Some(MagicAttrName::ValueParser) | Some(MagicAttrName::Action) => {
863 let expr = attr.value_or_abort()?;
864 self.push_method(*attr.kind.get(), attr.name.clone(), expr);
865 }
866
867 // Directives that never receive a value
868 Some(MagicAttrName::ValueEnum)
869 | Some(MagicAttrName::VerbatimDocComment) => {
870 let expr = attr.value_or_abort()?;
871 abort!(expr, "attribute `{}` does not accept a value", attr.name);
872 }
873
874 // Kinds
875 Some(MagicAttrName::FromGlobal)
876 | Some(MagicAttrName::Subcommand)
877 | Some(MagicAttrName::ExternalSubcommand)
878 | Some(MagicAttrName::Flatten)
879 | Some(MagicAttrName::Skip) => {
880 }
881 }
882 }
883
884 if self.has_explicit_methods() {
885 if let Kind::Skip(_, attr) = &*self.kind {
886 abort!(
887 self.methods[0].name.span(),
888 "`{}` cannot be used with `#[{}(skip)]",
889 self.methods[0].name,
890 attr.as_str(),
891 );
892 }
893 if let Kind::FromGlobal(_) = &*self.kind {
894 abort!(
895 self.methods[0].name.span(),
896 "`{}` cannot be used with `#[arg(from_global)]",
897 self.methods[0].name,
898 );
899 }
900 }
901
902 Ok(())
903 }
904
push_doc_comment(&mut self, attrs: &[Attribute], short_name: &str, long_name: Option<&str>)905 fn push_doc_comment(&mut self, attrs: &[Attribute], short_name: &str, long_name: Option<&str>) {
906 let lines = extract_doc_comment(attrs);
907
908 if !lines.is_empty() {
909 let (short_help, long_help) =
910 format_doc_comment(&lines, !self.verbatim_doc_comment, self.force_long_help);
911 let short_name = format_ident!("{}", short_name);
912 let short = Method::new(
913 short_name,
914 short_help
915 .map(|h| quote!(#h))
916 .unwrap_or_else(|| quote!(None)),
917 );
918 self.doc_comment.push(short);
919 if let Some(long_name) = long_name {
920 let long_name = format_ident!("{}", long_name);
921 let long = Method::new(
922 long_name,
923 long_help
924 .map(|h| quote!(#h))
925 .unwrap_or_else(|| quote!(None)),
926 );
927 self.doc_comment.push(long);
928 }
929 }
930 }
931
set_kind(&mut self, kind: Sp<Kind>) -> Result<(), syn::Error>932 fn set_kind(&mut self, kind: Sp<Kind>) -> Result<(), syn::Error> {
933 match (self.kind.get(), kind.get()) {
934 (Kind::Arg(_), Kind::FromGlobal(_))
935 | (Kind::Arg(_), Kind::Subcommand(_))
936 | (Kind::Arg(_), Kind::Flatten(_))
937 | (Kind::Arg(_), Kind::Skip(_, _))
938 | (Kind::Command(_), Kind::Subcommand(_))
939 | (Kind::Command(_), Kind::Flatten(_))
940 | (Kind::Command(_), Kind::Skip(_, _))
941 | (Kind::Command(_), Kind::ExternalSubcommand)
942 | (Kind::Value, Kind::Skip(_, _)) => {
943 self.kind = kind;
944 }
945
946 (_, _) => {
947 let old = self.kind.name();
948 let new = kind.name();
949 abort!(kind.span(), "`{}` cannot be used with `{}`", new, old);
950 }
951 }
952 Ok(())
953 }
954
find_default_method(&self) -> Option<&Method>955 pub fn find_default_method(&self) -> Option<&Method> {
956 self.methods
957 .iter()
958 .find(|m| m.name == "default_value" || m.name == "default_value_os")
959 }
960
961 /// generate methods from attributes on top of struct or enum
initial_top_level_methods(&self) -> TokenStream962 pub fn initial_top_level_methods(&self) -> TokenStream {
963 let next_display_order = self.next_display_order.as_ref().into_iter();
964 let next_help_heading = self.next_help_heading.as_ref().into_iter();
965 quote!(
966 #(#next_display_order)*
967 #(#next_help_heading)*
968 )
969 }
970
final_top_level_methods(&self) -> TokenStream971 pub fn final_top_level_methods(&self) -> TokenStream {
972 let methods = &self.methods;
973 let doc_comment = &self.doc_comment;
974
975 quote!( #(#doc_comment)* #(#methods)*)
976 }
977
978 /// generate methods on top of a field
field_methods(&self) -> proc_macro2::TokenStream979 pub fn field_methods(&self) -> proc_macro2::TokenStream {
980 let methods = &self.methods;
981 let doc_comment = &self.doc_comment;
982 quote!( #(#doc_comment)* #(#methods)* )
983 }
984
group_id(&self) -> TokenStream985 pub fn group_id(&self) -> TokenStream {
986 self.group_id.clone().raw()
987 }
988
group_methods(&self) -> TokenStream989 pub fn group_methods(&self) -> TokenStream {
990 let group_methods = &self.group_methods;
991 quote!( #(#group_methods)* )
992 }
993
deprecations(&self) -> proc_macro2::TokenStream994 pub fn deprecations(&self) -> proc_macro2::TokenStream {
995 let deprecations = &self.deprecations;
996 quote!( #(#deprecations)* )
997 }
998
next_display_order(&self) -> TokenStream999 pub fn next_display_order(&self) -> TokenStream {
1000 let next_display_order = self.next_display_order.as_ref().into_iter();
1001 quote!( #(#next_display_order)* )
1002 }
1003
next_help_heading(&self) -> TokenStream1004 pub fn next_help_heading(&self) -> TokenStream {
1005 let next_help_heading = self.next_help_heading.as_ref().into_iter();
1006 quote!( #(#next_help_heading)* )
1007 }
1008
id(&self) -> TokenStream1009 pub fn id(&self) -> TokenStream {
1010 self.name.clone().raw()
1011 }
1012
cased_name(&self) -> TokenStream1013 pub fn cased_name(&self) -> TokenStream {
1014 self.name.clone().translate(*self.casing)
1015 }
1016
value_name(&self) -> TokenStream1017 pub fn value_name(&self) -> TokenStream {
1018 self.name.clone().translate(CasingStyle::ScreamingSnake)
1019 }
1020
value_parser(&self, field_type: &Type) -> Method1021 pub fn value_parser(&self, field_type: &Type) -> Method {
1022 self.value_parser
1023 .clone()
1024 .map(|p| {
1025 let inner_type = inner_type(field_type);
1026 p.resolve(inner_type)
1027 })
1028 .unwrap_or_else(|| {
1029 let inner_type = inner_type(field_type);
1030 if let Some(action) = self.action.as_ref() {
1031 let span = action.span();
1032 default_value_parser(inner_type, span)
1033 } else {
1034 let span = self
1035 .action
1036 .as_ref()
1037 .map(|a| a.span())
1038 .unwrap_or_else(|| self.kind.span());
1039 default_value_parser(inner_type, span)
1040 }
1041 })
1042 }
1043
action(&self, field_type: &Type) -> Method1044 pub fn action(&self, field_type: &Type) -> Method {
1045 self.action
1046 .clone()
1047 .map(|p| p.resolve(field_type))
1048 .unwrap_or_else(|| {
1049 if let Some(value_parser) = self.value_parser.as_ref() {
1050 let span = value_parser.span();
1051 default_action(field_type, span)
1052 } else {
1053 let span = self
1054 .value_parser
1055 .as_ref()
1056 .map(|a| a.span())
1057 .unwrap_or_else(|| self.kind.span());
1058 default_action(field_type, span)
1059 }
1060 })
1061 }
1062
kind(&self) -> Sp<Kind>1063 pub fn kind(&self) -> Sp<Kind> {
1064 self.kind.clone()
1065 }
1066
is_positional(&self) -> bool1067 pub fn is_positional(&self) -> bool {
1068 self.is_positional
1069 }
1070
casing(&self) -> Sp<CasingStyle>1071 pub fn casing(&self) -> Sp<CasingStyle> {
1072 self.casing
1073 }
1074
env_casing(&self) -> Sp<CasingStyle>1075 pub fn env_casing(&self) -> Sp<CasingStyle> {
1076 self.env_casing
1077 }
1078
has_explicit_methods(&self) -> bool1079 pub fn has_explicit_methods(&self) -> bool {
1080 self.methods
1081 .iter()
1082 .any(|m| m.name != "help" && m.name != "long_help")
1083 }
1084
skip_group(&self) -> bool1085 pub fn skip_group(&self) -> bool {
1086 self.skip_group
1087 }
1088 }
1089
1090 #[derive(Clone)]
1091 enum ValueParser {
1092 Explicit(Method),
1093 Implicit(Ident),
1094 }
1095
1096 impl ValueParser {
resolve(self, _inner_type: &Type) -> Method1097 fn resolve(self, _inner_type: &Type) -> Method {
1098 match self {
1099 Self::Explicit(method) => method,
1100 Self::Implicit(ident) => default_value_parser(_inner_type, ident.span()),
1101 }
1102 }
1103
span(&self) -> Span1104 fn span(&self) -> Span {
1105 match self {
1106 Self::Explicit(method) => method.name.span(),
1107 Self::Implicit(ident) => ident.span(),
1108 }
1109 }
1110 }
1111
default_value_parser(inner_type: &Type, span: Span) -> Method1112 fn default_value_parser(inner_type: &Type, span: Span) -> Method {
1113 let func = Ident::new("value_parser", span);
1114 Method::new(
1115 func,
1116 quote_spanned! { span=>
1117 clap::value_parser!(#inner_type)
1118 },
1119 )
1120 }
1121
1122 #[derive(Clone)]
1123 pub enum Action {
1124 Explicit(Method),
1125 Implicit(Ident),
1126 }
1127
1128 impl Action {
resolve(self, _field_type: &Type) -> Method1129 pub fn resolve(self, _field_type: &Type) -> Method {
1130 match self {
1131 Self::Explicit(method) => method,
1132 Self::Implicit(ident) => default_action(_field_type, ident.span()),
1133 }
1134 }
1135
span(&self) -> Span1136 pub fn span(&self) -> Span {
1137 match self {
1138 Self::Explicit(method) => method.name.span(),
1139 Self::Implicit(ident) => ident.span(),
1140 }
1141 }
1142 }
1143
default_action(field_type: &Type, span: Span) -> Method1144 fn default_action(field_type: &Type, span: Span) -> Method {
1145 let ty = Ty::from_syn_ty(field_type);
1146 let args = match *ty {
1147 Ty::Vec | Ty::OptionVec | Ty::VecVec | Ty::OptionVecVec => {
1148 quote_spanned! { span=>
1149 clap::ArgAction::Append
1150 }
1151 }
1152 Ty::Option | Ty::OptionOption => {
1153 quote_spanned! { span=>
1154 clap::ArgAction::Set
1155 }
1156 }
1157 _ => {
1158 if is_simple_ty(field_type, "bool") {
1159 quote_spanned! { span=>
1160 clap::ArgAction::SetTrue
1161 }
1162 } else {
1163 quote_spanned! { span=>
1164 clap::ArgAction::Set
1165 }
1166 }
1167 }
1168 };
1169
1170 let func = Ident::new("action", span);
1171 Method::new(func, args)
1172 }
1173
1174 #[allow(clippy::large_enum_variant)]
1175 #[derive(Clone)]
1176 pub enum Kind {
1177 Arg(Sp<Ty>),
1178 Command(Sp<Ty>),
1179 Value,
1180 FromGlobal(Sp<Ty>),
1181 Subcommand(Sp<Ty>),
1182 Flatten(Sp<Ty>),
1183 Skip(Option<AttrValue>, AttrKind),
1184 ExternalSubcommand,
1185 }
1186
1187 impl Kind {
name(&self) -> &'static str1188 pub fn name(&self) -> &'static str {
1189 match self {
1190 Self::Arg(_) => "arg",
1191 Self::Command(_) => "command",
1192 Self::Value => "value",
1193 Self::FromGlobal(_) => "from_global",
1194 Self::Subcommand(_) => "subcommand",
1195 Self::Flatten(_) => "flatten",
1196 Self::Skip(_, _) => "skip",
1197 Self::ExternalSubcommand => "external_subcommand",
1198 }
1199 }
1200
attr_kind(&self) -> AttrKind1201 pub fn attr_kind(&self) -> AttrKind {
1202 match self {
1203 Self::Arg(_) => AttrKind::Arg,
1204 Self::Command(_) => AttrKind::Command,
1205 Self::Value => AttrKind::Value,
1206 Self::FromGlobal(_) => AttrKind::Arg,
1207 Self::Subcommand(_) => AttrKind::Command,
1208 Self::Flatten(_) => AttrKind::Command,
1209 Self::Skip(_, kind) => *kind,
1210 Self::ExternalSubcommand => AttrKind::Command,
1211 }
1212 }
1213
ty(&self) -> Option<&Sp<Ty>>1214 pub fn ty(&self) -> Option<&Sp<Ty>> {
1215 match self {
1216 Self::Arg(ty)
1217 | Self::Command(ty)
1218 | Self::Flatten(ty)
1219 | Self::FromGlobal(ty)
1220 | Self::Subcommand(ty) => Some(ty),
1221 Self::Value | Self::Skip(_, _) | Self::ExternalSubcommand => None,
1222 }
1223 }
1224 }
1225
1226 #[derive(Clone)]
1227 pub struct Method {
1228 name: Ident,
1229 args: TokenStream,
1230 }
1231
1232 impl Method {
new(name: Ident, args: TokenStream) -> Self1233 pub fn new(name: Ident, args: TokenStream) -> Self {
1234 Method { name, args }
1235 }
1236
from_env(ident: Ident, env_var: &str) -> Result<Option<Self>, syn::Error>1237 fn from_env(ident: Ident, env_var: &str) -> Result<Option<Self>, syn::Error> {
1238 let mut lit = match env::var(env_var) {
1239 Ok(val) => {
1240 if val.is_empty() {
1241 return Ok(None);
1242 }
1243 LitStr::new(&val, ident.span())
1244 }
1245 Err(_) => {
1246 abort!(
1247 ident,
1248 "cannot derive `{}` from Cargo.toml\n\n= note: {note}\n\n= help: {help}\n\n",
1249 ident,
1250 note = format_args!("`{}` environment variable is not set", env_var),
1251 help = format_args!("use `{} = \"...\"` to set {} manually", ident, ident)
1252 );
1253 }
1254 };
1255
1256 if ident == "author" {
1257 let edited = process_author_str(&lit.value());
1258 lit = LitStr::new(&edited, lit.span());
1259 }
1260
1261 Ok(Some(Method::new(ident, quote!(#lit))))
1262 }
1263
args(&self) -> &TokenStream1264 pub(crate) fn args(&self) -> &TokenStream {
1265 &self.args
1266 }
1267 }
1268
1269 impl ToTokens for Method {
to_tokens(&self, ts: &mut proc_macro2::TokenStream)1270 fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
1271 let Method { ref name, ref args } = self;
1272
1273 let tokens = quote!( .#name(#args) );
1274
1275 tokens.to_tokens(ts);
1276 }
1277 }
1278
1279 #[derive(Clone)]
1280 pub struct Deprecation {
1281 pub span: Span,
1282 pub id: &'static str,
1283 pub version: &'static str,
1284 pub description: String,
1285 }
1286
1287 impl Deprecation {
attribute(version: &'static str, old: AttrKind, new: AttrKind, span: Span) -> Self1288 fn attribute(version: &'static str, old: AttrKind, new: AttrKind, span: Span) -> Self {
1289 Self {
1290 span,
1291 id: "old_attribute",
1292 version,
1293 description: format!(
1294 "Attribute `#[{}(...)]` has been deprecated in favor of `#[{}(...)]`",
1295 old.as_str(),
1296 new.as_str()
1297 ),
1298 }
1299 }
1300 }
1301
1302 impl ToTokens for Deprecation {
to_tokens(&self, ts: &mut proc_macro2::TokenStream)1303 fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
1304 let tokens = if cfg!(feature = "deprecated") {
1305 let Deprecation {
1306 span,
1307 id,
1308 version,
1309 description,
1310 } = self;
1311 let span = *span;
1312 let id = Ident::new(id, span);
1313
1314 quote_spanned!(span=> {
1315 #[deprecated(since = #version, note = #description)]
1316 fn #id() {}
1317 #id();
1318 })
1319 } else {
1320 quote!()
1321 };
1322
1323 tokens.to_tokens(ts);
1324 }
1325 }
1326
assert_attr_kind(attr: &ClapAttr, possible_kind: &[AttrKind]) -> Result<(), syn::Error>1327 fn assert_attr_kind(attr: &ClapAttr, possible_kind: &[AttrKind]) -> Result<(), syn::Error> {
1328 if *attr.kind.get() == AttrKind::Clap || *attr.kind.get() == AttrKind::StructOpt {
1329 // deprecated
1330 } else if !possible_kind.contains(attr.kind.get()) {
1331 let options = possible_kind
1332 .iter()
1333 .map(|k| format!("`#[{}({})]`", k.as_str(), attr.name))
1334 .collect::<Vec<_>>();
1335 abort!(
1336 attr.name,
1337 "Unknown `#[{}({})]` attribute ({} exists)",
1338 attr.kind.as_str(),
1339 attr.name,
1340 options.join(", ")
1341 );
1342 }
1343 Ok(())
1344 }
1345
1346 /// replace all `:` with `, ` when not inside the `<>`
1347 ///
1348 /// `"author1:author2:author3" => "author1, author2, author3"`
1349 /// `"author1 <http://website1.com>:author2" => "author1 <http://website1.com>, author2"
process_author_str(author: &str) -> String1350 fn process_author_str(author: &str) -> String {
1351 let mut res = String::with_capacity(author.len());
1352 let mut inside_angle_braces = 0usize;
1353
1354 for ch in author.chars() {
1355 if inside_angle_braces > 0 && ch == '>' {
1356 inside_angle_braces -= 1;
1357 res.push(ch);
1358 } else if ch == '<' {
1359 inside_angle_braces += 1;
1360 res.push(ch);
1361 } else if inside_angle_braces == 0 && ch == ':' {
1362 res.push_str(", ");
1363 } else {
1364 res.push(ch);
1365 }
1366 }
1367
1368 res
1369 }
1370
1371 /// Defines the casing for the attributes long representation.
1372 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
1373 pub enum CasingStyle {
1374 /// Indicate word boundaries with uppercase letter, excluding the first word.
1375 Camel,
1376 /// Keep all letters lowercase and indicate word boundaries with hyphens.
1377 Kebab,
1378 /// Indicate word boundaries with uppercase letter, including the first word.
1379 Pascal,
1380 /// Keep all letters uppercase and indicate word boundaries with underscores.
1381 ScreamingSnake,
1382 /// Keep all letters lowercase and indicate word boundaries with underscores.
1383 Snake,
1384 /// Keep all letters lowercase and remove word boundaries.
1385 Lower,
1386 /// Keep all letters uppercase and remove word boundaries.
1387 Upper,
1388 /// Use the original attribute name defined in the code.
1389 Verbatim,
1390 }
1391
1392 impl CasingStyle {
from_lit(name: &LitStr) -> Result<Sp<Self>, syn::Error>1393 fn from_lit(name: &LitStr) -> Result<Sp<Self>, syn::Error> {
1394 use self::CasingStyle::*;
1395
1396 let normalized = name.value().to_upper_camel_case().to_lowercase();
1397 let cs = |kind| Sp::new(kind, name.span());
1398
1399 let s = match normalized.as_ref() {
1400 "camel" | "camelcase" => cs(Camel),
1401 "kebab" | "kebabcase" => cs(Kebab),
1402 "pascal" | "pascalcase" => cs(Pascal),
1403 "screamingsnake" | "screamingsnakecase" => cs(ScreamingSnake),
1404 "snake" | "snakecase" => cs(Snake),
1405 "lower" | "lowercase" => cs(Lower),
1406 "upper" | "uppercase" => cs(Upper),
1407 "verbatim" | "verbatimcase" => cs(Verbatim),
1408 s => abort!(name, "unsupported casing: `{}`", s),
1409 };
1410 Ok(s)
1411 }
1412 }
1413
1414 #[derive(Clone)]
1415 pub enum Name {
1416 Derived(Ident),
1417 Assigned(TokenStream),
1418 }
1419
1420 impl Name {
raw(self) -> TokenStream1421 pub fn raw(self) -> TokenStream {
1422 match self {
1423 Name::Assigned(tokens) => tokens,
1424 Name::Derived(ident) => {
1425 let s = ident.unraw().to_string();
1426 quote_spanned!(ident.span()=> #s)
1427 }
1428 }
1429 }
1430
translate(self, style: CasingStyle) -> TokenStream1431 pub fn translate(self, style: CasingStyle) -> TokenStream {
1432 use CasingStyle::*;
1433
1434 match self {
1435 Name::Assigned(tokens) => tokens,
1436 Name::Derived(ident) => {
1437 let s = ident.unraw().to_string();
1438 let s = match style {
1439 Pascal => s.to_upper_camel_case(),
1440 Kebab => s.to_kebab_case(),
1441 Camel => s.to_lower_camel_case(),
1442 ScreamingSnake => s.to_shouty_snake_case(),
1443 Snake => s.to_snake_case(),
1444 Lower => s.to_snake_case().replace('_', ""),
1445 Upper => s.to_shouty_snake_case().replace('_', ""),
1446 Verbatim => s,
1447 };
1448 quote_spanned!(ident.span()=> #s)
1449 }
1450 }
1451 }
1452
translate_char(self, style: CasingStyle) -> TokenStream1453 pub fn translate_char(self, style: CasingStyle) -> TokenStream {
1454 use CasingStyle::*;
1455
1456 match self {
1457 Name::Assigned(tokens) => quote!( (#tokens).chars().next().unwrap() ),
1458 Name::Derived(ident) => {
1459 let s = ident.unraw().to_string();
1460 let s = match style {
1461 Pascal => s.to_upper_camel_case(),
1462 Kebab => s.to_kebab_case(),
1463 Camel => s.to_lower_camel_case(),
1464 ScreamingSnake => s.to_shouty_snake_case(),
1465 Snake => s.to_snake_case(),
1466 Lower => s.to_snake_case(),
1467 Upper => s.to_shouty_snake_case(),
1468 Verbatim => s,
1469 };
1470
1471 let s = s.chars().next().unwrap();
1472 quote_spanned!(ident.span()=> #s)
1473 }
1474 }
1475 }
1476 }
1477