strum_macros-0.19.2/.cargo_vcs_info.json0000644000000001121371270423100137330ustar00{ "git": { "sha1": "520b2e6305a9fa0bf60754285f34f32aaf8f4976" } } strum_macros-0.19.2/Cargo.toml0000644000000031171371270423100117410ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "strum_macros" version = "0.19.2" authors = ["Peter Glotfelty "] description = "Helpful macros for working with enums and strings" homepage = "https://github.com/Peternator7/strum" documentation = "https://docs.rs/strum" readme = "../README.md" keywords = ["enum", "string", "macros", "proc-macros"] categories = ["development-tools::procedural-macro-helpers", "parsing"] license = "MIT" repository = "https://github.com/Peternator7/strum" [lib] name = "strum_macros" proc-macro = true [dependencies.heck] version = "0.3" [dependencies.proc-macro2] version = "1.0" [dependencies.quote] version = "1.0" [dependencies.syn] version = "1.0" features = ["parsing", "extra-traits"] [features] verbose-asrefstr-name = [] verbose-asstaticstr-name = [] verbose-display-name = [] verbose-enumcount-name = [] verbose-enumdiscriminants-name = [] verbose-enumiter-name = [] verbose-enummessage-name = [] verbose-enumproperty-name = [] verbose-enumstring-name = [] verbose-intostaticstr-name = [] verbose-tostring-name = [] verbose-variant-names = [] strum_macros-0.19.2/Cargo.toml.orig010066600000000000000000000020031371270365200154040ustar0000000000000000[package] name = "strum_macros" version = "0.19.2" authors = ["Peter Glotfelty "] license = "MIT" description = "Helpful macros for working with enums and strings" keywords = ["enum", "string", "macros", "proc-macros"] categories = ["development-tools::procedural-macro-helpers", "parsing"] documentation = "https://docs.rs/strum" homepage = "https://github.com/Peternator7/strum" repository = "https://github.com/Peternator7/strum" readme = "../README.md" [lib] proc-macro = true name = "strum_macros" [dependencies] heck = "0.3" proc-macro2 = "1.0" quote = "1.0" syn = { version = "1.0", features = ["parsing", "extra-traits"] } [features] verbose-enumstring-name = [] verbose-asrefstr-name = [] verbose-variant-names = [] verbose-asstaticstr-name = [] verbose-intostaticstr-name = [] verbose-tostring-name = [] verbose-display-name = [] verbose-enumiter-name = [] verbose-enummessage-name = [] verbose-enumproperty-name = [] verbose-enumdiscriminants-name = [] verbose-enumcount-name = []strum_macros-0.19.2/LICENSE010066600000000000000000000020601370720215300135170ustar0000000000000000MIT License Copyright (c) 2019 Peter Glotfelty Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. strum_macros-0.19.2/src/helpers/case_style.rs010066600000000000000000000065371370720215300174610ustar0000000000000000use heck::{CamelCase, KebabCase, MixedCase, ShoutySnakeCase, SnakeCase, TitleCase}; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum CaseStyle { CamelCase, KebabCase, MixedCase, ShoutySnakeCase, SnakeCase, TitleCase, UpperCase, LowerCase, ScreamingKebabCase, PascalCase, } impl<'s> From<&'s str> for CaseStyle { fn from(text: &'s str) -> CaseStyle { match text { "camel_case" | "PascalCase" => CaseStyle::PascalCase, "camelCase" => CaseStyle::CamelCase, "snake_case" | "snek_case" => CaseStyle::SnakeCase, "kebab_case" | "kebab-case" => CaseStyle::KebabCase, "SCREAMING-KEBAB-CASE" => CaseStyle::ScreamingKebabCase, "shouty_snake_case" | "shouty_snek_case" | "SCREAMING_SNAKE_CASE" => { CaseStyle::ShoutySnakeCase } "title_case" => CaseStyle::TitleCase, "mixed_case" => CaseStyle::MixedCase, "lowercase" => CaseStyle::LowerCase, "UPPERCASE" => CaseStyle::UpperCase, _ => panic!( "Unexpected case style for serialize_all: `{}`. Valid values are: `{:?}`", text, [ "camelCase", "PascalCase", "kebab-case", "snake_case", "SCREAMING_SNAKE_CASE", "SCREAMING-KEBAB-CASE", "lowercase", "UPPERCASE", "title_case", "mixed_case", ] ), } } } pub trait CaseStyleHelpers { fn convert_case(&self, case_style: Option) -> String; } impl CaseStyleHelpers for syn::Ident { fn convert_case(&self, case_style: Option) -> String { let ident_string = self.to_string(); if let Some(case_style) = case_style { match case_style { CaseStyle::PascalCase => ident_string.to_camel_case(), CaseStyle::KebabCase => ident_string.to_kebab_case(), CaseStyle::MixedCase => ident_string.to_mixed_case(), CaseStyle::ShoutySnakeCase => ident_string.to_shouty_snake_case(), CaseStyle::SnakeCase => ident_string.to_snake_case(), CaseStyle::TitleCase => ident_string.to_title_case(), CaseStyle::UpperCase => ident_string.to_uppercase(), CaseStyle::LowerCase => ident_string.to_lowercase(), CaseStyle::ScreamingKebabCase => ident_string.to_kebab_case().to_uppercase(), CaseStyle::CamelCase => { let camel_case = ident_string.to_camel_case(); let mut pascal = String::with_capacity(camel_case.len()); let mut it = camel_case.chars(); if let Some(ch) = it.next() { pascal.extend(ch.to_lowercase()); } pascal.extend(it); pascal } } } else { ident_string } } } #[test] fn test_convert_case() { let id = syn::Ident::new("test_me", proc_macro2::Span::call_site()); assert_eq!("testMe", id.convert_case(Some(CaseStyle::CamelCase))); assert_eq!("TestMe", id.convert_case(Some(CaseStyle::PascalCase))); } strum_macros-0.19.2/src/helpers/has_metadata.rs010066600000000000000000000025431371264526500177450ustar0000000000000000///Represents a type that can have strum metadata associated with it. pub trait HasMetadata { /// Get all the metadata associated with a specific "tag". /// All of strum's metadata is nested inside a path such as /// #[strum(...)] so this let's us quickly filter down to only our metadata. fn get_metadata(&self, ident: &str) -> Vec; } fn get_metadata_inner<'a>( ident: &str, it: impl IntoIterator, ) -> Vec { it.into_iter() .filter_map(|attr| attr.parse_meta().ok()) .filter_map(|meta| match meta { syn::Meta::List(syn::MetaList { path, nested, .. }) => { if path.is_ident(ident) { Some(nested) } else { None } } _ => None, }) .flat_map(|id| id) .map(|nested| match nested { syn::NestedMeta::Meta(meta) => meta, _ => panic!("unexpected literal parsing strum attributes"), }) .collect() } impl HasMetadata for syn::Variant { fn get_metadata(&self, ident: &str) -> Vec { get_metadata_inner(ident, &self.attrs) } } impl HasMetadata for syn::DeriveInput { fn get_metadata(&self, ident: &str) -> Vec { get_metadata_inner(ident, &self.attrs) } } strum_macros-0.19.2/src/helpers/meta_helpers.rs010066600000000000000000000030501371057151700177700ustar0000000000000000use syn::{Meta, MetaList, NestedMeta}; pub trait MetaHelpers { fn expect_metalist(&self, msg: &str) -> &MetaList; fn expect_path(&self, msg: &str) -> &syn::Path; fn expect_namevalue(&self, msg: &str) -> &syn::MetaNameValue; } impl MetaHelpers for syn::Meta { fn expect_metalist(&self, msg: &str) -> &MetaList { match self { Meta::List(list) => list, _ => panic!("{}", msg), } } fn expect_path(&self, msg: &str) -> &syn::Path { match self { Meta::Path(path) => path, _ => panic!("{}", msg), } } fn expect_namevalue(&self, msg: &str) -> &syn::MetaNameValue { match self { Meta::NameValue(pair) => pair, _ => panic!("{}", msg), } } } pub trait NestedMetaHelpers { fn expect_meta(&self, msg: &str) -> &syn::Meta; fn expect_lit(&self, msg: &str) -> &syn::Lit; } impl NestedMetaHelpers for NestedMeta { fn expect_meta(&self, msg: &str) -> &Meta { match self { syn::NestedMeta::Meta(m) => m, _ => panic!("{}", msg), } } fn expect_lit(&self, msg: &str) -> &syn::Lit { match self { syn::NestedMeta::Lit(l) => l, _ => panic!("{}", msg), } } } pub trait LitHelpers { fn expect_string(&self, msg: &str) -> String; } impl LitHelpers for syn::Lit { fn expect_string(&self, msg: &str) -> String { match self { syn::Lit::Str(s) => s.value(), _ => panic!("{}", msg), } } } strum_macros-0.19.2/src/helpers/mod.rs010066600000000000000000000004771371264466600161210ustar0000000000000000pub use self::case_style::CaseStyleHelpers; pub use self::meta_helpers::{LitHelpers, MetaHelpers, NestedMetaHelpers}; pub use self::type_props::HasTypeProperties; pub use self::variant_props::HasStrumVariantProperties; pub mod case_style; mod has_metadata; mod meta_helpers; pub mod type_props; pub mod variant_props; strum_macros-0.19.2/src/helpers/type_props.rs010066600000000000000000000057171371264465500175460ustar0000000000000000use std::convert::From; use std::default::Default; use crate::helpers::case_style::CaseStyle; use crate::helpers::has_metadata::HasMetadata; use crate::helpers::{MetaHelpers, NestedMetaHelpers}; pub trait HasTypeProperties { fn get_type_properties(&self) -> StrumTypeProperties; } #[derive(Debug, Clone, Eq, PartialEq, Default)] pub struct StrumTypeProperties { pub case_style: Option, pub discriminant_derives: Vec, pub discriminant_name: Option, pub discriminant_others: Vec, } impl HasTypeProperties for syn::DeriveInput { fn get_type_properties(&self) -> StrumTypeProperties { let mut output = StrumTypeProperties::default(); let strum_meta = self.get_metadata("strum"); let discriminants_meta = self.get_metadata("strum_discriminants"); for meta in strum_meta { let meta = match meta { syn::Meta::NameValue(mv) => mv, _ => panic!("strum on types only supports key-values"), }; if meta.path.is_ident("serialize_all") { let style = match meta.lit { syn::Lit::Str(s) => s.value(), _ => panic!("expected string value for 'serialize_all'"), }; if output.case_style.is_some() { panic!("found multiple values of serialize_all"); } output.case_style = Some(CaseStyle::from(&*style)); } else { panic!("unrecognized attribue found on strum(..)"); } } for meta in discriminants_meta { match meta { syn::Meta::List(ref ls) => { if ls.path.is_ident("derive") { let paths = ls .nested .iter() .map(|meta| meta.expect_meta("unexpected literal").path().clone()); output.discriminant_derives.extend(paths); } else if ls.path.is_ident("name") { if ls.nested.len() != 1 { panic!("name expects exactly 1 value"); } let value = ls.nested.first().expect("unexpected error"); let name = value .expect_meta("unexpected literal") .expect_path("name must be an identifier"); if output.discriminant_name.is_some() { panic!("multiple occurrences of 'name'"); } output.discriminant_name = Some(name.clone()); } else { output.discriminant_others.push(meta); } } _ => { output.discriminant_others.push(meta); } } } output } } strum_macros-0.19.2/src/helpers/variant_props.rs010066600000000000000000000117541371264466600202310ustar0000000000000000use std::collections::HashMap; use std::default::Default; use crate::helpers::case_style::{CaseStyle, CaseStyleHelpers}; use crate::helpers::has_metadata::HasMetadata; use crate::helpers::{LitHelpers, MetaHelpers, NestedMetaHelpers}; pub trait HasStrumVariantProperties { fn get_variant_properties(&self) -> StrumVariantProperties; } #[derive(Clone, Eq, PartialEq, Debug, Default)] pub struct StrumVariantProperties { pub is_disabled: bool, pub default: bool, pub message: Option, pub detailed_message: Option, pub string_props: HashMap, serialize: Vec, to_string: Option, ident: Option, } impl StrumVariantProperties { pub fn get_preferred_name(&self, case_style: Option) -> String { if let Some(ref to_string) = self.to_string { to_string.clone() } else { let mut serialized = self.serialize.clone(); serialized.sort_by_key(|s| s.len()); if let Some(n) = serialized.pop() { n } else { self.ident .as_ref() .expect("identifier") .convert_case(case_style) } } } pub fn get_serializations(&self, case_style: Option) -> Vec { let mut attrs = self.serialize.clone(); if let Some(ref to_string) = self.to_string { attrs.push(to_string.clone()); } if attrs.is_empty() { attrs.push( self.ident .as_ref() .expect("identifier") .convert_case(case_style), ); } attrs } } impl HasStrumVariantProperties for syn::Variant { fn get_variant_properties(&self) -> StrumVariantProperties { let mut output = StrumVariantProperties::default(); output.ident = Some(self.ident.clone()); for meta in self.get_metadata("strum") { match meta { syn::Meta::NameValue(syn::MetaNameValue { path, lit, .. }) => { if path.is_ident("message") { if output.message.is_some() { panic!("message is set twice on the same variant"); } output.message = Some(lit.expect_string("expected string")); } else if path.is_ident("detailed_message") { if output.detailed_message.is_some() { panic!("detailed message set twice on the same variant"); } output.detailed_message = Some(lit.expect_string("expected string")); } else if path.is_ident("serialize") { output.serialize.push(lit.expect_string("expected string")); } else if path.is_ident("to_string") { if output.to_string.is_some() { panic!("to_string is set twice on the same variant"); } output.to_string = Some(lit.expect_string("expected string")); } else if path.is_ident("disabled") { panic!("this method is deprecated. Prefer #[strum(disabled)] instead of #[strum(disabled=\"true\")]"); } else if path.is_ident("default") { panic!("this method is deprecated. Prefer #[strum(default)] instead of #[strum(default=\"true\")]"); } else { panic!("unrecognized value in strum(..) attribute"); } } syn::Meta::Path(p) => { if p.is_ident("disabled") { output.is_disabled = true; } else if p.is_ident("default") { output.default = true; } else { panic!("unrecognized value in strum(..) attribute"); } } syn::Meta::List(syn::MetaList { path, nested, .. }) => { if path.is_ident("props") { for p in nested { let p = p .expect_meta("unexpected literal found in props") .expect_namevalue("props must be key-value pairs"); let key = p .path .get_ident() .expect("key must be an identifier") .to_string(); let value = p.lit.expect_string("expected string"); output.string_props.insert(key, value); } } else { panic!("unrecognized value in strum(..) attribute"); } } } } output } } strum_macros-0.19.2/src/lib.rs010066600000000000000000000147431370757101600144370ustar0000000000000000//! # Strum //! //! Strum is a set of macros and traits for working with //! enums and strings easier in Rust. //! //! # Documentation //! //! The documentation for this crate is found in the `strum` crate. #![recursion_limit = "128"] extern crate heck; #[macro_use] extern crate syn; #[macro_use] extern crate quote; extern crate proc_macro; extern crate proc_macro2; mod helpers; mod macros; use proc_macro2::TokenStream; use std::env; fn debug_print_generated(ast: &syn::DeriveInput, toks: &TokenStream) { let debug = env::var("STRUM_DEBUG"); if let Ok(s) = debug { if s == "1" { println!("{}", toks); } if ast.ident == s { println!("{}", toks); } } } #[cfg_attr( not(feature = "verbose-enumstring-name"), proc_macro_derive(EnumString, attributes(strum)) )] #[cfg_attr( feature = "verbose-enumstring-name", proc_macro_derive(StrumEnumString, attributes(strum)) )] pub fn from_string(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = macros::from_string::from_string_inner(&ast); debug_print_generated(&ast, &toks); toks.into() } #[cfg_attr( not(feature = "verbose-asrefstr-name"), proc_macro_derive(AsRefStr, attributes(strum)) )] #[cfg_attr( feature = "verbose-asrefstr-name", proc_macro_derive(StrumAsRefStr, attributes(strum)) )] pub fn as_ref_str(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = macros::as_ref_str::as_ref_str_inner(&ast); debug_print_generated(&ast, &toks); toks.into() } #[cfg_attr( not(feature = "verbose-variant-names"), proc_macro_derive(EnumVariantNames, attributes(strum)) )] #[cfg_attr( feature = "verbose-variant-names", proc_macro_derive(StrumEnumVariantNames, attributes(strum)) )] pub fn variant_names(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = macros::enum_variant_names::enum_variant_names_inner(&ast); debug_print_generated(&ast, &toks); toks.into() } #[cfg_attr( feature = "verbose-asstaticstr-name", proc_macro_derive(StrumAsStaticStr, attributes(strum)) )] #[cfg_attr( not(feature = "verbose-asstaticstr-name"), proc_macro_derive(AsStaticStr, attributes(strum)) )] pub fn as_static_str(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = macros::as_ref_str::as_static_str_inner( &ast, macros::as_ref_str::GenerateTraitVariant::AsStaticStr, ); debug_print_generated(&ast, &toks); toks.into() } #[cfg_attr( feature = "verbose-intostaticstr-name", proc_macro_derive(StrumIntoStaticStr, attributes(strum)) )] #[cfg_attr( not(feature = "verbose-intostaticstr-name"), proc_macro_derive(IntoStaticStr, attributes(strum)) )] pub fn into_static_str(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = macros::as_ref_str::as_static_str_inner( &ast, macros::as_ref_str::GenerateTraitVariant::From, ); debug_print_generated(&ast, &toks); toks.into() } #[cfg_attr( feature = "verbose-tostring-name", proc_macro_derive(StrumToString, attributes(strum)) )] #[cfg_attr( not(feature = "verbose-tostring-name"), proc_macro_derive(ToString, attributes(strum)) )] pub fn to_string(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = macros::to_string::to_string_inner(&ast); debug_print_generated(&ast, &toks); toks.into() } #[cfg_attr( feature = "verbose-display-name", proc_macro_derive(StrumDisplay, attributes(strum)) )] #[cfg_attr( not(feature = "verbose-display-name"), proc_macro_derive(Display, attributes(strum)) )] pub fn display(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = macros::display::display_inner(&ast); debug_print_generated(&ast, &toks); toks.into() } #[cfg_attr( feature = "verbose-enumiter-name", proc_macro_derive(StrumEnumIter, attributes(strum)) )] #[cfg_attr( not(feature = "verbose-enumiter-name"), proc_macro_derive(EnumIter, attributes(strum)) )] pub fn enum_iter(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = macros::enum_iter::enum_iter_inner(&ast); debug_print_generated(&ast, &toks); toks.into() } #[cfg_attr( feature = "verbose-enummessage-name", proc_macro_derive(StrumEnumMessage, attributes(strum)) )] #[cfg_attr( not(feature = "verbose-enummessage-name"), proc_macro_derive(EnumMessage, attributes(strum)) )] pub fn enum_messages(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = macros::enum_messages::enum_message_inner(&ast); debug_print_generated(&ast, &toks); toks.into() } #[cfg_attr( feature = "verbose-enumproperty-name", proc_macro_derive(StrumEnumProperty, attributes(strum)) )] #[cfg_attr( not(feature = "verbose-enumproperty-name"), proc_macro_derive(EnumProperty, attributes(strum)) )] pub fn enum_properties(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = macros::enum_properties::enum_properties_inner(&ast); debug_print_generated(&ast, &toks); toks.into() } #[cfg_attr( feature = "verbose-enumdiscriminants-name", proc_macro_derive(StrumEnumDiscriminants, attributes(strum, strum_discriminants)) )] #[cfg_attr( not(feature = "verbose-enumdiscriminants-name"), proc_macro_derive(EnumDiscriminants, attributes(strum, strum_discriminants)) )] pub fn enum_discriminants(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = macros::enum_discriminants::enum_discriminants_inner(&ast); debug_print_generated(&ast, &toks); toks.into() } #[cfg_attr( feature = "verbose-enumcount-name", proc_macro_derive(StrumEnumCount, attributes(strum)) )] #[cfg_attr( not(feature = "verbose-enumcount-name"), proc_macro_derive(EnumCount, attributes(strum)) )] pub fn enum_count(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = macros::enum_count::enum_count_inner(&ast); debug_print_generated(&ast, &toks); toks.into() } strum_macros-0.19.2/src/macros/enum_count.rs010066600000000000000000000012701371057151700173200ustar0000000000000000use proc_macro2::TokenStream; use syn; pub(crate) fn enum_count_inner(ast: &syn::DeriveInput) -> TokenStream { let n = match ast.data { syn::Data::Enum(ref v) => v.variants.len(), _ => panic!("EnumCount can only be used with enums"), }; // Used in the quasi-quotation below as `#name` let name = &ast.ident; // Helper is provided for handling complex generic types correctly and effortlessly let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); quote! { // Implementation impl #impl_generics ::strum::EnumCount for #name #ty_generics #where_clause { const COUNT: usize = #n; } } } strum_macros-0.19.2/src/macros/enum_discriminants.rs010066600000000000000000000107141371264465500210500ustar0000000000000000use proc_macro2::{Span, TokenStream}; use syn; use crate::helpers::HasTypeProperties; /// Attributes to copy from the main enum's variants to the discriminant enum's variants. /// /// Attributes not in this list may be for other `proc_macro`s on the main enum, and may cause /// compilation problems when copied across. const ATTRIBUTES_TO_COPY: &[&str] = &["doc", "cfg", "allow", "deny"]; pub fn enum_discriminants_inner(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; let vis = &ast.vis; let variants = match ast.data { syn::Data::Enum(ref v) => &v.variants, _ => panic!("EnumDiscriminants only works on Enums"), }; // Derives for the generated enum let type_properties = ast.get_type_properties(); let derives = type_properties.discriminant_derives; let derives = quote! { #[derive(Clone, Copy, Debug, PartialEq, Eq, #(#derives),*)] }; // Work out the name let default_name = syn::Path::from(syn::Ident::new( &format!("{}Discriminants", name.to_string()), Span::call_site(), )); let discriminants_name = type_properties.discriminant_name.unwrap_or(default_name); // Pass through all other attributes let pass_though_attributes = type_properties .discriminant_others .into_iter() .map(|meta| quote! { #[ #meta ] }) .collect::>(); // Add the variants without fields, but exclude the `strum` meta item let mut discriminants = Vec::new(); for variant in variants { let ident = &variant.ident; // Don't copy across the "strum" meta attribute. let attrs = variant.attrs.iter().filter(|attr| { ATTRIBUTES_TO_COPY .iter() .any(|attr_whitelisted| attr.path.is_ident(attr_whitelisted)) }); discriminants.push(quote! { #(#attrs)* #ident }); } // Ideally: // // * For `Copy` types, we `impl From for TheEnumDiscriminants` // * For `!Copy` types, we `impl<'enum> From<&'enum TheEnum> for TheEnumDiscriminants` // // That way we ensure users are not able to pass a `Copy` type by reference. However, the // `#[derive(..)]` attributes are not in the parsed tokens, so we are not able to check if a // type is `Copy`, so we just implement both. // // See // --- // let is_copy = unique_meta_list(type_meta.iter(), "derive") // .map(extract_list_metas) // .map(|metas| { // metas // .filter_map(get_meta_ident) // .any(|derive| derive.to_string() == "Copy") // }).unwrap_or(false); let arms = variants .iter() .map(|variant| { let ident = &variant.ident; use syn::Fields::*; let params = match variant.fields { Unit => quote! {}, Unnamed(ref _fields) => { quote! { (..) } } Named(ref _fields) => { quote! { { .. } } } }; quote! { #name::#ident #params => #discriminants_name::#ident } }) .collect::>(); let from_fn_body = quote! { match val { #(#arms),* } }; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let impl_from = quote! { impl #impl_generics ::std::convert::From< #name #ty_generics > for #discriminants_name #where_clause { fn from(val: #name #ty_generics) -> #discriminants_name { #from_fn_body } } }; let impl_from_ref = { let mut generics = ast.generics.clone(); let lifetime = parse_quote!('_enum); let enum_life = quote! { & #lifetime }; generics.params.push(lifetime); // Shadows the earlier `impl_generics` let (impl_generics, _, _) = generics.split_for_impl(); quote! { impl #impl_generics ::std::convert::From< #enum_life #name #ty_generics > for #discriminants_name #where_clause { fn from(val: #enum_life #name #ty_generics) -> #discriminants_name { #from_fn_body } } } }; quote! { /// Auto-generated discriminant enum variants #derives #(#pass_though_attributes)* #vis enum #discriminants_name { #(#discriminants),* } #impl_from #impl_from_ref } } strum_macros-0.19.2/src/macros/enum_iter.rs010066600000000000000000000120561371057151700171370ustar0000000000000000use proc_macro2::TokenStream; use syn; use crate::helpers::HasStrumVariantProperties; pub fn enum_iter_inner(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; let gen = &ast.generics; let (impl_generics, ty_generics, where_clause) = gen.split_for_impl(); let vis = &ast.vis; if gen.lifetimes().count() > 0 { panic!( "Enum Iterator isn't supported on Enums with lifetimes. The resulting enums would \ be unbounded." ); } let phantom_data = if gen.type_params().count() > 0 { let g = gen.type_params().map(|param| ¶m.ident); quote! { < ( #(#g),* ) > } } else { quote! { < () > } }; let variants = match ast.data { syn::Data::Enum(ref v) => &v.variants, _ => panic!("EnumIter only works on Enums"), }; let mut arms = Vec::new(); let enabled = variants .iter() .filter(|variant| !variant.get_variant_properties().is_disabled); for (idx, variant) in enabled.enumerate() { use syn::Fields::*; let ident = &variant.ident; let params = match variant.fields { Unit => quote! {}, Unnamed(ref fields) => { let defaults = ::std::iter::repeat(quote!(::std::default::Default::default())) .take(fields.unnamed.len()); quote! { (#(#defaults),*) } } Named(ref fields) => { let fields = fields .named .iter() .map(|field| field.ident.as_ref().unwrap()); quote! { {#(#fields: ::std::default::Default::default()),*} } } }; arms.push(quote! {#idx => ::std::option::Option::Some(#name::#ident #params)}); } let variant_count = arms.len(); arms.push(quote! { _ => ::std::option::Option::None }); let iter_name = syn::parse_str::(&format!("{}Iter", name)).unwrap(); quote! { #[allow(missing_docs)] #vis struct #iter_name #ty_generics { idx: usize, back_idx: usize, marker: ::std::marker::PhantomData #phantom_data, } impl #impl_generics #iter_name #ty_generics #where_clause { fn get(&self, idx: usize) -> Option<#name #ty_generics> { match idx { #(#arms),* } } } impl #impl_generics ::strum::IntoEnumIterator for #name #ty_generics #where_clause { type Iterator = #iter_name #ty_generics; fn iter() -> #iter_name #ty_generics { #iter_name { idx: 0, back_idx: 0, marker: ::std::marker::PhantomData, } } } impl #impl_generics Iterator for #iter_name #ty_generics #where_clause { type Item = #name #ty_generics; fn next(&mut self) -> Option { self.nth(0) } fn size_hint(&self) -> (usize, Option) { let t = if self.idx + self.back_idx >= #variant_count { 0 } else { #variant_count - self.idx - self.back_idx }; (t, Some(t)) } fn nth(&mut self, n: usize) -> Option { let idx = self.idx + n + 1; if idx + self.back_idx > #variant_count { // We went past the end of the iterator. Freeze idx at #variant_count // so that it doesn't overflow if the user calls this repeatedly. // See PR #76 for context. self.idx = #variant_count; None } else { self.idx = idx; self.get(idx - 1) } } } impl #impl_generics ExactSizeIterator for #iter_name #ty_generics #where_clause { fn len(&self) -> usize { self.size_hint().0 } } impl #impl_generics DoubleEndedIterator for #iter_name #ty_generics #where_clause { fn next_back(&mut self) -> Option { let back_idx = self.back_idx + 1; if self.idx + back_idx > #variant_count { // We went past the end of the iterator. Freeze back_idx at #variant_count // so that it doesn't overflow if the user calls this repeatedly. // See PR #76 for context. self.back_idx = #variant_count; None } else { self.back_idx = back_idx; self.get(#variant_count - self.back_idx) } } } impl #impl_generics Clone for #iter_name #ty_generics #where_clause { fn clone(&self) -> #iter_name #ty_generics { #iter_name { idx: self.idx, back_idx: self.back_idx, marker: self.marker.clone(), } } } } } strum_macros-0.19.2/src/macros/enum_messages.rs010066600000000000000000000061421371264466600200120ustar0000000000000000use proc_macro2::TokenStream; use syn; use crate::helpers::{HasStrumVariantProperties, HasTypeProperties}; pub fn enum_message_inner(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let variants = match ast.data { syn::Data::Enum(ref v) => &v.variants, _ => panic!("EnumMessage only works on Enums"), }; let type_properties = ast.get_type_properties(); let mut arms = Vec::new(); let mut detailed_arms = Vec::new(); let mut serializations = Vec::new(); for variant in variants { let variant_properties = variant.get_variant_properties(); let messages = variant_properties.message.as_ref(); let detailed_messages = variant_properties.detailed_message.as_ref(); let ident = &variant.ident; use syn::Fields::*; let params = match variant.fields { Unit => quote! {}, Unnamed(..) => quote! { (..) }, Named(..) => quote! { {..} }, }; // You can't disable getting the serializations. { let serialization_variants = variant_properties.get_serializations(type_properties.case_style); let count = serialization_variants.len(); serializations.push(quote! { &#name::#ident #params => { static ARR: [&'static str; #count] = [#(#serialization_variants),*]; &ARR } }); } // But you can disable the messages. if variant_properties.is_disabled { continue; } if let Some(msg) = messages { let params = params.clone(); // Push the simple message. let tokens = quote! { &#name::#ident #params => ::std::option::Option::Some(#msg) }; arms.push(tokens.clone()); if detailed_messages.is_none() { detailed_arms.push(tokens); } } if let Some(msg) = detailed_messages { let params = params.clone(); // Push the simple message. detailed_arms .push(quote! { &#name::#ident #params => ::std::option::Option::Some(#msg) }); } } if arms.len() < variants.len() { arms.push(quote! { _ => ::std::option::Option::None }); } if detailed_arms.len() < variants.len() { detailed_arms.push(quote! { _ => ::std::option::Option::None }); } quote! { impl #impl_generics ::strum::EnumMessage for #name #ty_generics #where_clause { fn get_message(&self) -> ::std::option::Option<&str> { match self { #(#arms),* } } fn get_detailed_message(&self) -> ::std::option::Option<&str> { match self { #(#detailed_arms),* } } fn get_serializations(&self) -> &[&str] { match self { #(#serializations),* } } } } } strum_macros-0.19.2/src/macros/enum_properties.rs010066600000000000000000000036711371057151700203730ustar0000000000000000use proc_macro2::TokenStream; use syn; use crate::helpers::HasStrumVariantProperties; pub fn enum_properties_inner(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let variants = match ast.data { syn::Data::Enum(ref v) => &v.variants, _ => panic!("EnumProp only works on Enums"), }; let mut arms = Vec::new(); for variant in variants { let ident = &variant.ident; let variant_properties = variant.get_variant_properties(); let mut string_arms = Vec::new(); let mut bool_arms = Vec::new(); let mut num_arms = Vec::new(); // But you can disable the messages. if variant_properties.is_disabled { continue; } use syn::Fields::*; let params = match variant.fields { Unit => quote! {}, Unnamed(..) => quote! { (..) }, Named(..) => quote! { {..} }, }; for (key, value) in variant_properties.string_props { string_arms.push(quote! { #key => ::std::option::Option::Some( #value )}) } string_arms.push(quote! { _ => ::std::option::Option::None }); bool_arms.push(quote! { _ => ::std::option::Option::None }); num_arms.push(quote! { _ => ::std::option::Option::None }); arms.push(quote! { &#name::#ident #params => { match prop { #(#string_arms),* } } }); } if arms.len() < variants.len() { arms.push(quote! { _ => ::std::option::Option::None }); } quote! { impl #impl_generics ::strum::EnumProperty for #name #ty_generics #where_clause { fn get_str(&self, prop: &str) -> ::std::option::Option<&'static str> { match self { #(#arms),* } } } } } strum_macros-0.19.2/src/macros/enum_variant_names.rs010066600000000000000000000016731371264466600210360ustar0000000000000000use proc_macro2::TokenStream; use syn; use crate::helpers::{HasStrumVariantProperties, HasTypeProperties}; pub fn enum_variant_names_inner(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; let gen = &ast.generics; let (impl_generics, ty_generics, where_clause) = gen.split_for_impl(); let variants = match ast.data { syn::Data::Enum(ref v) => &v.variants, _ => panic!("EnumVariantNames only works on Enums"), }; // Derives for the generated enum let type_properties = ast.get_type_properties(); let names = variants .iter() .map(|v| { v.get_variant_properties() .get_preferred_name(type_properties.case_style) }) .collect::>(); quote! { impl #impl_generics ::strum::VariantNames for #name #ty_generics #where_clause { const VARIANTS: &'static [&'static str] = &[ #(#names),* ]; } } } strum_macros-0.19.2/src/macros/mod.rs010066600000000000000000000004471370720215300157210ustar0000000000000000pub mod enum_count; pub mod enum_discriminants; pub mod enum_iter; pub mod enum_messages; pub mod enum_properties; pub mod enum_variant_names; mod strings; pub use self::strings::as_ref_str; pub use self::strings::display; pub use self::strings::from_string; pub use self::strings::to_string; strum_macros-0.19.2/src/macros/strings/as_ref_str.rs010066600000000000000000000071371371264466600210040ustar0000000000000000use proc_macro2::TokenStream; use syn; use crate::helpers::{HasStrumVariantProperties, HasTypeProperties}; fn get_arms(ast: &syn::DeriveInput) -> Vec { let name = &ast.ident; let mut arms = Vec::new(); let variants = match ast.data { syn::Data::Enum(ref v) => &v.variants, _ => panic!("This macro only works on Enums"), }; let type_properties = ast.get_type_properties(); for variant in variants { use syn::Fields::*; let ident = &variant.ident; let variant_properties = variant.get_variant_properties(); if variant_properties.is_disabled { continue; } // Look at all the serialize attributes. // Use `to_string` attribute (not `as_ref_str` or something) to keep things consistent // (i.e. always `enum.as_ref().to_string() == enum.to_string()`). let output = variant_properties.get_preferred_name(type_properties.case_style); let params = match variant.fields { Unit => quote! {}, Unnamed(..) => quote! { (..) }, Named(..) => quote! { {..} }, }; arms.push(quote! { #name::#ident #params => #output }); } if arms.len() < variants.len() { arms.push(quote! { _ => panic!("AsRef::::as_ref() or AsStaticRef::::as_static() \ called on disabled variant.") }) } arms } pub fn as_ref_str_inner(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let arms = get_arms(ast); quote! { impl #impl_generics ::std::convert::AsRef for #name #ty_generics #where_clause { fn as_ref(&self) -> &str { match *self { #(#arms),* } } } } } pub enum GenerateTraitVariant { AsStaticStr, From, } pub fn as_static_str_inner( ast: &syn::DeriveInput, trait_variant: GenerateTraitVariant, ) -> TokenStream { let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let arms = get_arms(ast); let mut generics = ast.generics.clone(); generics .params .push(syn::GenericParam::Lifetime(syn::LifetimeDef::new( parse_quote!('_derivative_strum), ))); let (impl_generics2, _, _) = generics.split_for_impl(); let arms2 = arms.clone(); let arms3 = arms.clone(); match trait_variant { GenerateTraitVariant::AsStaticStr => { quote! { impl #impl_generics ::strum::AsStaticRef for #name #ty_generics #where_clause { fn as_static(&self) -> &'static str { match *self { #(#arms),* } } } } } GenerateTraitVariant::From => { quote! { impl #impl_generics ::std::convert::From<#name #ty_generics> for &'static str #where_clause { fn from(x: #name #ty_generics) -> &'static str { match x { #(#arms2),* } } } impl #impl_generics2 ::std::convert::From<&'_derivative_strum #name #ty_generics> for &'static str #where_clause { fn from(x: &'_derivative_strum #name #ty_generics) -> &'static str { match *x { #(#arms3),* } } } } } } } strum_macros-0.19.2/src/macros/strings/display.rs010066600000000000000000000030651371264466600203160ustar0000000000000000use proc_macro2::TokenStream; use syn; use crate::helpers::{HasStrumVariantProperties, HasTypeProperties}; pub fn display_inner(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let variants = match ast.data { syn::Data::Enum(ref v) => &v.variants, _ => panic!("Display only works on Enums"), }; let type_properties = ast.get_type_properties(); let mut arms = Vec::new(); for variant in variants { let ident = &variant.ident; let variant_properties = variant.get_variant_properties(); if variant_properties.is_disabled { continue; } // Look at all the serialize attributes. let output = variant_properties.get_preferred_name(type_properties.case_style); let params = match variant.fields { syn::Fields::Unit => quote! {}, syn::Fields::Unnamed(..) => quote! { (..) }, syn::Fields::Named(..) => quote! { {..} }, }; arms.push(quote! { #name::#ident #params => f.pad(#output) }); } if arms.len() < variants.len() { arms.push(quote! { _ => panic!("fmt() called on disabled variant.")}) } quote! { impl #impl_generics ::std::fmt::Display for #name #ty_generics #where_clause { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> { match *self { #(#arms),* } } } } } strum_macros-0.19.2/src/macros/strings/from_string.rs010066600000000000000000000054101371264466600211760ustar0000000000000000use proc_macro2::TokenStream; use syn; use crate::helpers::{HasStrumVariantProperties, HasTypeProperties}; pub fn from_string_inner(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let variants = match ast.data { syn::Data::Enum(ref v) => &v.variants, _ => panic!("FromString only works on Enums"), }; let type_properties = ast.get_type_properties(); let mut has_default = false; let mut default = quote! { _ => ::std::result::Result::Err(::strum::ParseError::VariantNotFound) }; let mut arms = Vec::new(); for variant in variants { use syn::Fields::*; let ident = &variant.ident; let variant_properties = variant.get_variant_properties(); if variant_properties.is_disabled { continue; } if variant_properties.default { if has_default { panic!("Can't have multiple default variants"); } if let Unnamed(ref fields) = variant.fields { if fields.unnamed.len() != 1 { panic!("Default only works on unit structs with a single String parameter"); } default = quote! { default => ::std::result::Result::Ok(#name::#ident (default.into())) }; } else { panic!("Default only works on unit structs with a single String parameter"); } has_default = true; continue; } // If we don't have any custom variants, add the default serialized name. let attrs = variant_properties.get_serializations(type_properties.case_style); let params = match variant.fields { Unit => quote! {}, Unnamed(ref fields) => { let defaults = ::std::iter::repeat(quote!(Default::default())).take(fields.unnamed.len()); quote! { (#(#defaults),*) } } Named(ref fields) => { let fields = fields .named .iter() .map(|field| field.ident.as_ref().unwrap()); quote! { {#(#fields: Default::default()),*} } } }; arms.push(quote! { #(#attrs)|* => ::std::result::Result::Ok(#name::#ident #params) }); } arms.push(default); quote! { impl #impl_generics ::std::str::FromStr for #name #ty_generics #where_clause { type Err = ::strum::ParseError; fn from_str(s: &str) -> ::std::result::Result< #name #ty_generics , Self::Err> { match s { #(#arms),* } } } } } strum_macros-0.19.2/src/macros/strings/mod.rs010066600000000000000000000001151370720215300174020ustar0000000000000000pub mod as_ref_str; pub mod display; pub mod from_string; pub mod to_string; strum_macros-0.19.2/src/macros/strings/to_string.rs010066600000000000000000000030341371057151700206450ustar0000000000000000use proc_macro2::TokenStream; use syn; use crate::helpers::{HasStrumVariantProperties, HasTypeProperties}; pub fn to_string_inner(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let variants = match ast.data { syn::Data::Enum(ref v) => &v.variants, _ => panic!("ToString only works on Enums"), }; let type_properties = ast.get_type_properties(); let mut arms = Vec::new(); for variant in variants { use syn::Fields::*; let ident = &variant.ident; let variant_properties = variant.get_variant_properties(); if variant_properties.is_disabled { continue; } // Look at all the serialize attributes. let output = variant_properties.get_preferred_name(type_properties.case_style); let params = match variant.fields { Unit => quote! {}, Unnamed(..) => quote! { (..) }, Named(..) => quote! { {..} }, }; arms.push(quote! { #name::#ident #params => ::std::string::String::from(#output) }); } if arms.len() < variants.len() { arms.push(quote! { _ => panic!("to_string() called on disabled variant.")}) } quote! { impl #impl_generics ::std::string::ToString for #name #ty_generics #where_clause { fn to_string(&self) -> ::std::string::String { match *self { #(#arms),* } } } } }