1// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>, 2// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and 3// Ana Hobden (@hoverbear) <operator@hoverbear.org> 4// 5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or 6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license 7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your 8// option. This file may not be copied, modified, or distributed 9// except according to those terms. 10// 11// This work was derived from Structopt (https://github.com/TeXitoi/structopt) 12// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the 13// MIT/Apache 2.0 license. 14 15use std::env; 16 17use heck::{ToKebabCase, ToLowerCamelCase, ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase}; 18use proc_macro2::{self, Span, TokenStream}; 19use quote::{format_ident, quote, quote_spanned, ToTokens}; 20use syn::DeriveInput; 21use syn::{self, ext::IdentExt, spanned::Spanned, Attribute, Field, Ident, LitStr, Type, Variant}; 22 23use crate::attr::*; 24use crate::utils::{extract_doc_comment, format_doc_comment, inner_type, is_simple_ty, Sp, Ty}; 25 26/// Default casing style for generated arguments. 27pub const DEFAULT_CASING: CasingStyle = CasingStyle::Kebab; 28 29/// Default casing style for environment variables 30pub const DEFAULT_ENV_CASING: CasingStyle = CasingStyle::ScreamingSnake; 31 32#[derive(Clone)] 33pub struct Item { 34 name: Name, 35 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 54impl Item { 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 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 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 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 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 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 249 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 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 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 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 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 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 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 948 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 957 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 965 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 971 pub fn deprecations(&self) -> proc_macro2::TokenStream { 972 let deprecations = &self.deprecations; 973 quote!( #(#deprecations)* ) 974 } 975 976 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 981 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 986 pub fn ident(&self) -> &Ident { 987 &self.ident 988 } 989 990 pub fn id(&self) -> TokenStream { 991 self.name.clone().raw() 992 } 993 994 pub fn cased_name(&self) -> TokenStream { 995 self.name.clone().translate(*self.casing) 996 } 997 998 pub fn value_name(&self) -> TokenStream { 999 self.name.clone().translate(CasingStyle::ScreamingSnake) 1000 } 1001 1002 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 1025 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 1044 pub fn kind(&self) -> Sp<Kind> { 1045 self.kind.clone() 1046 } 1047 1048 pub fn is_positional(&self) -> bool { 1049 self.is_positional 1050 } 1051 1052 pub fn casing(&self) -> Sp<CasingStyle> { 1053 self.casing 1054 } 1055 1056 pub fn env_casing(&self) -> Sp<CasingStyle> { 1057 self.env_casing 1058 } 1059 1060 pub fn has_explicit_methods(&self) -> bool { 1061 self.methods 1062 .iter() 1063 .any(|m| m.name != "help" && m.name != "long_help") 1064 } 1065 1066 pub fn skip_group(&self) -> bool { 1067 self.skip_group 1068 } 1069} 1070 1071#[derive(Clone)] 1072enum ValueParser { 1073 Explicit(Method), 1074 Implicit(Ident), 1075} 1076 1077impl ValueParser { 1078 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 1085 fn span(&self) -> Span { 1086 match self { 1087 Self::Explicit(method) => method.name.span(), 1088 Self::Implicit(ident) => ident.span(), 1089 } 1090 } 1091} 1092 1093fn 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)] 1104pub enum Action { 1105 Explicit(Method), 1106 Implicit(Ident), 1107} 1108 1109impl Action { 1110 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 1117 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 1125fn 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)] 1157pub 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 1168impl Kind { 1169 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 1182 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 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)] 1208pub struct Method { 1209 name: Ident, 1210 args: TokenStream, 1211} 1212 1213impl Method { 1214 pub fn new(name: Ident, args: TokenStream) -> Self { 1215 Method { name, args } 1216 } 1217 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 1245 pub(crate) fn args(&self) -> &TokenStream { 1246 &self.args 1247 } 1248} 1249 1250impl ToTokens for Method { 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)] 1261pub struct Deprecation { 1262 pub span: Span, 1263 pub id: &'static str, 1264 pub version: &'static str, 1265 pub description: String, 1266} 1267 1268impl Deprecation { 1269 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 1283impl ToTokens for Deprecation { 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 1308fn 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" 1331fn 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)] 1354pub 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 1373impl CasingStyle { 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)] 1396pub enum Name { 1397 Derived(Ident), 1398 Assigned(TokenStream), 1399} 1400 1401impl Name { 1402 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 1412 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 1434 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