1
0
mirror of https://github.com/danog/strum.git synced 2024-12-02 09:27:57 +01:00

Merge branch 'master' into peternator7/0.19

This commit is contained in:
Peter Glotfelty 🚀 2020-07-30 16:22:02 +00:00
commit f997ee6c86
29 changed files with 406 additions and 319 deletions

View File

@ -8,6 +8,27 @@
* **Breaking Change**: Use Associated Constant for EnumCount instead of const fn and free constant. [#99](https://github.com/Peternator7/strum/pull/99)
* **Breaking Change**: EnumVariantNames now properly adjusts to the `to_string` and `serialize` attributes.
This behavior is consistent with the other derives.
* **Breaking Change**. `default` and `disabled` should now be written as markers instead of key value pairs.
Here is the old way of adding these attributes to a variant.
```rust
// OLD WAY
enum Test {
#[strum(disabled = "true", default = "true")]
Variant(String)
}
```
Here is the new way. There is less ambiguity in the new syntax.
```rust
enum Test {
#[strum(disabled, default)]
Variant(String)
}
```
* **Breaking Change**. Most of the strum plugins will now error more aggresively on invalid options being
used. Historically, the plugins have ignore invalid options, but most of these should error now. Silent
errors are a rust anti-pattern.
## 0.18.0

View File

@ -192,12 +192,12 @@ pub trait EnumMessage {
/// }
/// ```
pub trait EnumProperty {
fn get_str(&self, &str) -> Option<&'static str>;
fn get_int(&self, &str) -> Option<usize> {
fn get_str(&self, prop: &str) -> Option<&'static str>;
fn get_int(&self, _prop: &str) -> Option<usize> {
Option::None
}
fn get_bool(&self, &str) -> Option<bool> {
fn get_bool(&self, _prop: &str) -> Option<bool> {
Option::None
}
}
@ -215,7 +215,7 @@ where
/// A trait for capturing the number of variants in Enum. This trait can be autoderived by
/// `strum_macros`.
pub trait EnumCount {
fn count() -> usize;
const COUNT: usize;
}
/// A trait for retrieving the names of each variant in Enum. This trait can

View File

@ -10,6 +10,7 @@ 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]

View File

@ -0,0 +1,43 @@
///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<syn::Meta>;
}
fn get_metadata_inner<'a>(
ident: &str,
it: impl IntoIterator<Item = &'a syn::Attribute>,
) -> Vec<syn::Meta> {
it.into_iter()
.map(|attr| attr.parse_meta().unwrap())
.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<syn::Meta> {
get_metadata_inner(ident, &self.attrs)
}
}
impl HasMetadata for syn::DeriveInput {
fn get_metadata(&self, ident: &str) -> Vec<syn::Meta> {
get_metadata_inner(ident, &self.attrs)
}
}

View File

@ -1,30 +1,63 @@
use syn::{Meta, MetaList};
use syn::{Meta, MetaList, NestedMeta};
pub trait MetaHelpers {
fn try_metalist(&self) -> Option<&MetaList>;
fn try_path(&self) -> Option<&syn::Path>;
fn try_namevalue(&self) -> Option<&syn::MetaNameValue>;
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 try_metalist(&self) -> Option<&MetaList> {
fn expect_metalist(&self, msg: &str) -> &MetaList {
match self {
Meta::List(list) => Some(list),
_ => None,
Meta::List(list) => list,
_ => panic!("{}", msg),
}
}
fn try_path(&self) -> Option<&syn::Path> {
fn expect_path(&self, msg: &str) -> &syn::Path {
match self {
Meta::Path(path) => Some(path),
_ => None,
Meta::Path(path) => path,
_ => panic!("{}", msg),
}
}
fn try_namevalue(&self) -> Option<&syn::MetaNameValue> {
fn expect_namevalue(&self, msg: &str) -> &syn::MetaNameValue {
match self {
Meta::NameValue(pair) => Some(pair),
_ => None,
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),
}
}
}

View File

@ -1,66 +0,0 @@
use super::MetaHelpers;
use super::MetaListHelpers;
use syn::Meta;
pub trait MetaIteratorHelpers {
fn find_attribute(&self, attr: &str) -> std::vec::IntoIter<&Meta>;
fn find_properties(&self, attr: &str, prop: &str) -> Vec<String>;
fn find_unique_property(&self, attr: &str, prop: &str) -> Option<String> {
let mut curr = self.find_properties(attr, prop);
if curr.len() > 1 {
panic!("More than one property: {} found on variant", prop);
}
curr.pop()
}
fn is_disabled(&self) -> bool {
let v = self.find_properties("strum", "disabled");
match v.len() {
0 => false,
1 => v[0] == "true",
_ => panic!("Can't have multiple values for 'disabled'"),
}
}
}
//impl MetaIteratorHelpers for [Meta]
impl<T> MetaIteratorHelpers for [T]
where
T: std::borrow::Borrow<Meta>,
{
fn find_attribute(&self, attr: &str) -> std::vec::IntoIter<&Meta> {
self.iter()
.filter_map(|meta| meta.borrow().try_metalist())
.filter(|list| list.path.is_ident(attr))
.flat_map(|list| list.expand_inner())
.collect::<Vec<_>>()
.into_iter()
}
fn find_properties(&self, attr: &str, prop: &str) -> Vec<String> {
use syn::{Lit, MetaNameValue};
self.iter()
// Only look at MetaList style attributes `[strum(...)]`
.filter_map(|meta| meta.borrow().try_metalist())
.filter(|list| list.path.is_ident(attr))
.flat_map(|list| list.expand_inner())
// Match all the properties with a given ident `[strum(serialize = "value")]`
.filter_map(|meta| match *meta {
Meta::NameValue(MetaNameValue {
ref path,
lit: Lit::Str(ref s),
..
}) => {
if path.is_ident(prop) {
Some(s.value())
} else {
None
}
}
_ => None,
})
.collect()
}
}

View File

@ -1,18 +0,0 @@
use syn::Meta;
pub trait MetaListHelpers {
fn expand_inner(&self) -> Vec<&Meta>;
}
impl MetaListHelpers for syn::MetaList {
fn expand_inner(&self) -> Vec<&Meta> {
use syn::NestedMeta;
self.nested
.iter()
.filter_map(|nested| match *nested {
NestedMeta::Meta(ref meta) => Some(meta),
_ => None,
})
.collect()
}
}

View File

@ -1,18 +1,11 @@
use syn::{Attribute, Meta};
pub 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;
pub mod type_props;
pub mod variant_props;
mod has_metadata;
mod meta_helpers;
mod meta_iterator_helpers;
mod metalist_helpers;
pub use self::case_style::CaseStyleHelpers;
pub use self::meta_helpers::MetaHelpers;
pub use self::meta_iterator_helpers::MetaIteratorHelpers;
pub use self::metalist_helpers::MetaListHelpers;
pub fn extract_meta(attrs: &[Attribute]) -> Vec<Meta> {
attrs
.iter()
.filter_map(|attribute| attribute.parse_meta().ok())
.collect()
}

View File

@ -0,0 +1,86 @@
use 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<CaseStyle>,
pub discriminant_derives: Vec<syn::Path>,
pub discriminant_name: Option<syn::Path>,
pub discriminant_others: Vec<syn::Meta>,
}
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
}
}

View File

@ -0,0 +1,123 @@
use 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<String>,
pub detailed_message: Option<String>,
pub string_props: HashMap<String, String>,
serialize: Vec<String>,
to_string: Option<String>,
ident: Option<syn::Ident>,
}
impl StrumVariantProperties {
pub fn get_preferred_name(&self, case_style: Option<CaseStyle>) -> 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<CaseStyle>) -> Vec<String> {
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
}
}

View File

@ -1,4 +1,4 @@
use proc_macro2::{Span, TokenStream};
use proc_macro2::TokenStream;
use syn;
pub(crate) fn enum_count_inner(ast: &syn::DeriveInput) -> TokenStream {
@ -9,23 +9,14 @@ pub(crate) fn enum_count_inner(ast: &syn::DeriveInput) -> TokenStream {
// Used in the quasi-quotation below as `#name`
let name = &ast.ident;
let const_name = &syn::Ident::new(
&format!("{}_COUNT", name.to_string().to_uppercase()),
Span::call_site(),
);
// 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 {
fn count() -> usize {
#n
}
}
#[allow(dead_code, missing_docs)]
pub const #const_name: usize = #n;
// Implementation
impl #impl_generics ::strum::EnumCount for #name #ty_generics #where_clause {
const COUNT: usize = #n;
}
}
}

View File

@ -1,8 +1,7 @@
use crate::helpers::{MetaHelpers, MetaIteratorHelpers, MetaListHelpers};
use proc_macro2::{Span, TokenStream};
use syn;
use helpers::extract_meta;
use crate::helpers::HasTypeProperties;
/// Attributes to copy from the main enum's variants to the discriminant enum's variants.
///
@ -20,15 +19,9 @@ pub fn enum_discriminants_inner(ast: &syn::DeriveInput) -> TokenStream {
};
// Derives for the generated enum
let type_meta = extract_meta(&ast.attrs);
let discriminant_attrs = type_meta
.find_attribute("strum_discriminants")
.collect::<Vec<&syn::Meta>>();
let type_properties = ast.get_type_properties();
let derives = discriminant_attrs
.find_attribute("derive")
.map(|meta| meta.path())
.collect::<Vec<_>>();
let derives = type_properties.discriminant_derives;
let derives = quote! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, #(#derives),*)]
@ -40,28 +33,12 @@ pub fn enum_discriminants_inner(ast: &syn::DeriveInput) -> TokenStream {
Span::call_site(),
));
let discriminants_name = discriminant_attrs
.iter()
.filter_map(|meta| meta.try_metalist())
.filter(|list| list.path.is_ident("name"))
// We want exactly zero or one items. Start with the assumption we have zero, i.e. None
// Then set our output to the first value we see. If fold is called again and we already
// have a value, panic.
.fold(None, |acc, val| match acc {
Some(_) => panic!("Expecting a single attribute 'name' in EnumDiscriminants."),
None => Some(val),
})
.map(|meta| meta.expand_inner())
.and_then(|metas| metas.into_iter().map(|meta| meta.path()).next())
.unwrap_or(&default_name);
let discriminants_name = type_properties.discriminant_name.unwrap_or(default_name);
// Pass through all other attributes
let pass_though_attributes = discriminant_attrs
.iter()
.filter(|meta| {
let path = meta.path();
!path.is_ident("derive") && !path.is_ident("name")
})
let pass_though_attributes = type_properties
.discriminant_others
.into_iter()
.map(|meta| quote! { #[ #meta ] })
.collect::<Vec<_>>();

View File

@ -1,7 +1,7 @@
use proc_macro2::TokenStream;
use syn;
use helpers::{extract_meta, MetaIteratorHelpers};
use crate::helpers::HasStrumVariantProperties;
pub fn enum_iter_inner(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
@ -31,7 +31,7 @@ pub fn enum_iter_inner(ast: &syn::DeriveInput) -> TokenStream {
let mut arms = Vec::new();
let enabled = variants
.iter()
.filter(|variant| !extract_meta(&variant.attrs).is_disabled());
.filter(|variant| !variant.get_variant_properties().is_disabled);
for (idx, variant) in enabled.enumerate() {
use syn::Fields::*;

View File

@ -1,8 +1,7 @@
use proc_macro2::TokenStream;
use syn;
use crate::helpers::case_style::CaseStyle;
use helpers::{extract_meta, CaseStyleHelpers, MetaIteratorHelpers};
use crate::helpers::{HasStrumVariantProperties, HasTypeProperties};
pub fn enum_message_inner(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
@ -12,19 +11,16 @@ pub fn enum_message_inner(ast: &syn::DeriveInput) -> TokenStream {
_ => panic!("EnumMessage only works on Enums"),
};
let type_meta = extract_meta(&ast.attrs);
let case_style = type_meta
.find_unique_property("strum", "serialize_all")
.map(|style| CaseStyle::from(style.as_ref()));
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 meta = extract_meta(&variant.attrs);
let messages = meta.find_unique_property("strum", "message");
let detailed_messages = meta.find_unique_property("strum", "detailed_message");
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::*;
@ -36,10 +32,7 @@ pub fn enum_message_inner(ast: &syn::DeriveInput) -> TokenStream {
// You can't disable getting the serializations.
{
let mut serialization_variants = meta.find_properties("strum", "serialize");
if serialization_variants.len() == 0 {
serialization_variants.push(ident.convert_case(case_style));
}
let serialization_variants = variant_properties.get_serializations(type_properties.case_style);
let count = serialization_variants.len();
serializations.push(quote! {
@ -51,7 +44,7 @@ pub fn enum_message_inner(ast: &syn::DeriveInput) -> TokenStream {
}
// But you can disable the messages.
if meta.is_disabled() {
if variant_properties.is_disabled {
continue;
}

View File

@ -1,28 +1,7 @@
use proc_macro2::TokenStream;
use syn;
use syn::Meta;
use crate::helpers::{extract_meta, MetaHelpers, MetaIteratorHelpers, MetaListHelpers};
fn extract_properties(meta: &[Meta]) -> Vec<(&syn::Path, &syn::Lit)> {
meta.iter()
// Filter down to the strum(..) attribute
.filter_map(|meta| meta.try_metalist())
.filter(|list| list.path.is_ident("strum"))
.flat_map(|list| list.expand_inner())
// Filter down to the `strum(props(..))` attribute
.filter_map(|meta| meta.try_metalist())
.filter(|inner_list| inner_list.path.is_ident("props"))
.flat_map(|inner_list| inner_list.expand_inner())
// Expand all the pairs `strum(props(key = value, ..))`
.filter_map(|prop| match *prop {
syn::Meta::NameValue(syn::MetaNameValue {
ref path, ref lit, ..
}) => Some((path, lit)),
_ => None,
})
.collect()
}
use crate::helpers::HasStrumVariantProperties;
pub fn enum_properties_inner(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
@ -35,12 +14,12 @@ pub fn enum_properties_inner(ast: &syn::DeriveInput) -> TokenStream {
let mut arms = Vec::new();
for variant in variants {
let ident = &variant.ident;
let meta = extract_meta(&variant.attrs);
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 meta.is_disabled() {
if variant_properties.is_disabled {
continue;
}
@ -51,17 +30,8 @@ pub fn enum_properties_inner(ast: &syn::DeriveInput) -> TokenStream {
Named(..) => quote! { {..} },
};
for (key, value) in extract_properties(&meta) {
use syn::Lit::*;
let key = key.segments.last().unwrap().ident.to_string();
match value {
Str(ref s, ..) => {
string_arms.push(quote! { #key => ::std::option::Option::Some( #s )})
}
Bool(b) => bool_arms.push(quote! { #key => ::std::option::Option::Some( #b )}),
Int(i, ..) => num_arms.push(quote! { #key => ::std::option::Option::Some( #i )}),
_ => {}
}
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 });

View File

@ -1,7 +1,7 @@
use proc_macro2::TokenStream;
use syn;
use crate::helpers::{case_style::CaseStyle, extract_meta, CaseStyleHelpers, MetaIteratorHelpers};
use crate::helpers::{CaseStyleHelpers, HasTypeProperties};
pub fn enum_variant_names_inner(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
@ -14,14 +14,11 @@ pub fn enum_variant_names_inner(ast: &syn::DeriveInput) -> TokenStream {
};
// Derives for the generated enum
let type_meta = extract_meta(&ast.attrs);
let case_style = type_meta
.find_unique_property("strum", "serialize_all")
.map(|style| CaseStyle::from(style.as_ref()));
let type_properties = ast.get_type_properties();
let names = variants
.iter()
.map(|v| v.ident.convert_case(case_style))
.map(|v| v.ident.convert_case(type_properties.case_style))
.collect::<Vec<_>>();
quote! {

View File

@ -1,8 +1,7 @@
use proc_macro2::TokenStream;
use syn;
use crate::helpers::case_style::CaseStyle;
use helpers::{extract_meta, CaseStyleHelpers, MetaIteratorHelpers};
use crate::helpers::{HasTypeProperties, HasStrumVariantProperties};
fn get_arms(ast: &syn::DeriveInput) -> Vec<TokenStream> {
let name = &ast.ident;
@ -12,36 +11,21 @@ fn get_arms(ast: &syn::DeriveInput) -> Vec<TokenStream> {
_ => panic!("This macro only works on Enums"),
};
let type_meta = extract_meta(&ast.attrs);
let case_style = type_meta
.find_unique_property("strum", "serialize_all")
.map(|style| CaseStyle::from(style.as_ref()));
let type_properties = ast.get_type_properties();
for variant in variants {
use syn::Fields::*;
let ident = &variant.ident;
let meta = extract_meta(&variant.attrs);
let variant_properties = variant.get_variant_properties();
if meta.is_disabled() {
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 = if let Some(n) = meta.find_unique_property("strum", "to_string") {
n
} else {
let mut attrs = meta.find_properties("strum", "serialize");
// We always take the longest one. This is arbitary, but is *mostly* deterministic
attrs.sort_by_key(|s| s.len());
if let Some(n) = attrs.pop() {
n
} else {
ident.convert_case(case_style)
}
};
let output = variant_properties.get_preferred_name(type_properties.case_style);
let params = match variant.fields {
Unit => quote! {},
Unnamed(..) => quote! { (..) },

View File

@ -1,8 +1,7 @@
use proc_macro2::TokenStream;
use syn;
use crate::helpers::case_style::CaseStyle;
use helpers::{extract_meta, CaseStyleHelpers, MetaIteratorHelpers};
use crate::helpers::{HasTypeProperties, HasStrumVariantProperties};
pub fn display_inner(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
@ -12,39 +11,24 @@ pub fn display_inner(ast: &syn::DeriveInput) -> TokenStream {
_ => panic!("Display only works on Enums"),
};
let type_meta = extract_meta(&ast.attrs);
let case_style = type_meta
.find_unique_property("strum", "serialize_all")
.map(|style| CaseStyle::from(style.as_ref()));
let type_properties = ast.get_type_properties();
let mut arms = Vec::new();
for variant in variants {
use syn::Fields::*;
let ident = &variant.ident;
let meta = extract_meta(&variant.attrs);
let variant_properties = variant.get_variant_properties();
if meta.is_disabled() {
if variant_properties.is_disabled {
continue;
}
// Look at all the serialize attributes.
let output = if let Some(n) = meta.find_unique_property("strum", "to_string") {
n
} else {
let mut attrs = meta.find_properties("strum", "serialize");
// We always take the longest one. This is arbitary, but is *mostly* deterministic
attrs.sort_by_key(|s| s.len());
if let Some(n) = attrs.pop() {
n
} else {
ident.convert_case(case_style)
}
};
let output = variant_properties.get_preferred_name(type_properties.case_style);
let params = match variant.fields {
Unit => quote! {},
Unnamed(..) => quote! { (..) },
Named(..) => quote! { {..} },
syn::Fields::Unit => quote! {},
syn::Fields::Unnamed(..) => quote! { (..) },
syn::Fields::Named(..) => quote! { {..} },
};
arms.push(quote! { #name::#ident #params => f.pad(#output) });

View File

@ -1,8 +1,7 @@
use proc_macro2::TokenStream;
use syn;
use crate::helpers::case_style::CaseStyle;
use crate::helpers::{extract_meta, CaseStyleHelpers, MetaIteratorHelpers};
use crate::helpers::{HasTypeProperties, HasStrumVariantProperties};
pub fn from_string_inner(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
@ -12,10 +11,7 @@ pub fn from_string_inner(ast: &syn::DeriveInput) -> TokenStream {
_ => panic!("FromString only works on Enums"),
};
let type_meta = extract_meta(&ast.attrs);
let case_style = type_meta
.find_unique_property("strum", "serialize_all")
.map(|style| CaseStyle::from(style.as_ref()));
let type_properties = ast.get_type_properties();
let mut has_default = false;
let mut default =
@ -24,24 +20,13 @@ pub fn from_string_inner(ast: &syn::DeriveInput) -> TokenStream {
for variant in variants {
use syn::Fields::*;
let ident = &variant.ident;
let meta = extract_meta(&variant.attrs);
let variant_properties = variant.get_variant_properties();
// Look at all the serialize attributes.
// let mut attrs = find_properties(&meta, "strum", "serialize");
// attrs.extend(find_properties(&meta, "strum", "to_string"));
let mut attrs = meta.find_properties("strum", "serialize");
attrs.extend(meta.find_properties("strum", "to_string"));
// if is_disabled(&meta) {
if meta.is_disabled() {
if variant_properties.is_disabled {
continue;
}
if meta
.find_unique_property("strum", "default")
.map_or(false, |s| s == "true")
{
if variant_properties.default {
if has_default {
panic!("Can't have multiple default variants");
}
@ -63,9 +48,7 @@ pub fn from_string_inner(ast: &syn::DeriveInput) -> TokenStream {
}
// If we don't have any custom variants, add the default serialized name.
if attrs.len() == 0 {
attrs.push(ident.convert_case(case_style));
}
let attrs = variant_properties.get_serializations(type_properties.case_style);
let params = match variant.fields {
Unit => quote! {},

View File

@ -1,8 +1,7 @@
use proc_macro2::TokenStream;
use syn;
use crate::helpers::case_style::CaseStyle;
use crate::helpers::{extract_meta, CaseStyleHelpers, MetaIteratorHelpers};
use crate::helpers::{HasStrumVariantProperties, HasTypeProperties};
pub fn to_string_inner(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
@ -12,34 +11,19 @@ pub fn to_string_inner(ast: &syn::DeriveInput) -> TokenStream {
_ => panic!("ToString only works on Enums"),
};
let type_meta = extract_meta(&ast.attrs);
let case_style = type_meta
.find_unique_property("strum", "serialize_all")
.map(|style| CaseStyle::from(style.as_ref()));
let type_properties = ast.get_type_properties();
let mut arms = Vec::new();
for variant in variants {
use syn::Fields::*;
let ident = &variant.ident;
let meta = extract_meta(&variant.attrs);
let variant_properties = variant.get_variant_properties();
if meta.is_disabled() {
if variant_properties.is_disabled {
continue;
}
// Look at all the serialize attributes.
let output = if let Some(n) = meta.find_unique_property("strum", "to_string") {
n
} else {
let mut attrs = meta.find_properties("strum", "serialize");
// We always take the longest one. This is arbitary, but is *mostly* deterministic
attrs.sort_by_key(|s| s.len());
if let Some(n) = attrs.pop() {
n
} else {
ident.convert_case(case_style)
}
};
let output = variant_properties.get_preferred_name(type_properties.case_style);
let params = match variant.fields {
Unit => quote! {},

View File

@ -14,6 +14,6 @@ pub enum Color {
Blue { hue: usize },
#[strum(serialize = "y", serialize = "yellow")]
Yellow,
#[strum(disabled = "true")]
#[strum(disabled)]
Green(String),
}

View File

@ -12,7 +12,7 @@ enum Color {
Blue { hue: usize },
#[strum(serialize = "y", serialize = "yellow")]
Yellow,
#[strum(disabled = "true")]
#[strum(disabled)]
Green(String),
}

View File

@ -9,7 +9,7 @@ enum Color {
Blue { hue: usize },
#[strum(serialize = "y", serialize = "yellow")]
Yellow,
#[strum(default = "true")]
#[strum(default)]
Green(String),
}

View File

@ -13,7 +13,7 @@ enum Color {
Blue { hue: usize },
#[strum(serialize = "y", serialize = "yellow")]
Yellow,
#[strum(default = "true")]
#[strum(default)]
Green(String),
}

View File

@ -10,7 +10,7 @@ enum Color {
Blue { hue: usize },
#[strum(serialize = "y", serialize = "yellow")]
Yellow,
#[strum(default = "true")]
#[strum(default)]
Green(String),
}
@ -21,9 +21,18 @@ fn to_blue_string() {
#[test]
fn test_formatters() {
assert_eq!(String::from(" blue"), format!("{:>6}", Color::Blue { hue: 0 }));
assert_eq!(String::from("blue "), format!("{:<6}", Color::Blue { hue: 0 }));
assert_eq!(String::from(" blue "), format!("{:^6}", Color::Blue { hue: 0 }));
assert_eq!(
String::from(" blue"),
format!("{:>6}", Color::Blue { hue: 0 })
);
assert_eq!(
String::from("blue "),
format!("{:<6}", Color::Blue { hue: 0 })
);
assert_eq!(
String::from(" blue "),
format!("{:^6}", Color::Blue { hue: 0 })
);
assert_eq!(String::from("bl"), format!("{:.2}", Color::Blue { hue: 0 }));
}

View File

@ -17,7 +17,6 @@ enum Week {
#[test]
fn simple_test() {
assert_eq!(7, Week::count());
assert_eq!(Week::count(), WEEK_COUNT);
assert_eq!(Week::iter().count(), WEEK_COUNT);
assert_eq!(7, Week::COUNT);
assert_eq!(Week::iter().count(), Week::COUNT);
}

View File

@ -15,7 +15,7 @@ enum Pets {
#[strum(detailed_message = "My fish is named Charles McFish")]
Fish,
Bird,
#[strum(disabled = "true")]
#[strum(disabled)]
Hamster,
}

View File

@ -12,7 +12,7 @@ enum Color {
},
#[strum(serialize = "y", serialize = "yellow")]
Yellow,
#[strum(default = "true")]
#[strum(default)]
Green(String),
#[strum(to_string = "purp")]
Purple,

View File

@ -13,7 +13,7 @@ enum Color {
Blue { hue: usize },
#[strum(serialize = "y", serialize = "yellow")]
Yellow,
#[strum(default = "true")]
#[strum(default)]
Green(String),
}