mirror of
https://github.com/danog/strum.git
synced 2024-12-03 09:57:55 +01:00
Merge branch 'master' into peternator7/0.19
This commit is contained in:
commit
f997ee6c86
21
CHANGELOG.md
21
CHANGELOG.md
@ -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**: 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.
|
* **Breaking Change**: EnumVariantNames now properly adjusts to the `to_string` and `serialize` attributes.
|
||||||
This behavior is consistent with the other derives.
|
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
|
## 0.18.0
|
||||||
|
|
||||||
|
@ -192,12 +192,12 @@ pub trait EnumMessage {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub trait EnumProperty {
|
pub trait EnumProperty {
|
||||||
fn get_str(&self, &str) -> Option<&'static str>;
|
fn get_str(&self, prop: &str) -> Option<&'static str>;
|
||||||
fn get_int(&self, &str) -> Option<usize> {
|
fn get_int(&self, _prop: &str) -> Option<usize> {
|
||||||
Option::None
|
Option::None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_bool(&self, &str) -> Option<bool> {
|
fn get_bool(&self, _prop: &str) -> Option<bool> {
|
||||||
Option::None
|
Option::None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -215,7 +215,7 @@ where
|
|||||||
/// A trait for capturing the number of variants in Enum. This trait can be autoderived by
|
/// A trait for capturing the number of variants in Enum. This trait can be autoderived by
|
||||||
/// `strum_macros`.
|
/// `strum_macros`.
|
||||||
pub trait EnumCount {
|
pub trait EnumCount {
|
||||||
fn count() -> usize;
|
const COUNT: usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait for retrieving the names of each variant in Enum. This trait can
|
/// A trait for retrieving the names of each variant in Enum. This trait can
|
||||||
|
@ -10,6 +10,7 @@ categories = ["development-tools::procedural-macro-helpers", "parsing"]
|
|||||||
|
|
||||||
documentation = "https://docs.rs/strum"
|
documentation = "https://docs.rs/strum"
|
||||||
homepage = "https://github.com/Peternator7/strum"
|
homepage = "https://github.com/Peternator7/strum"
|
||||||
|
repository = "https://github.com/Peternator7/strum"
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
43
strum_macros/src/helpers/has_metadata.rs
Normal file
43
strum_macros/src/helpers/has_metadata.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
@ -1,30 +1,63 @@
|
|||||||
use syn::{Meta, MetaList};
|
use syn::{Meta, MetaList, NestedMeta};
|
||||||
|
|
||||||
pub trait MetaHelpers {
|
pub trait MetaHelpers {
|
||||||
fn try_metalist(&self) -> Option<&MetaList>;
|
fn expect_metalist(&self, msg: &str) -> &MetaList;
|
||||||
fn try_path(&self) -> Option<&syn::Path>;
|
fn expect_path(&self, msg: &str) -> &syn::Path;
|
||||||
fn try_namevalue(&self) -> Option<&syn::MetaNameValue>;
|
fn expect_namevalue(&self, msg: &str) -> &syn::MetaNameValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MetaHelpers for syn::Meta {
|
impl MetaHelpers for syn::Meta {
|
||||||
fn try_metalist(&self) -> Option<&MetaList> {
|
fn expect_metalist(&self, msg: &str) -> &MetaList {
|
||||||
match self {
|
match self {
|
||||||
Meta::List(list) => Some(list),
|
Meta::List(list) => list,
|
||||||
_ => None,
|
_ => panic!("{}", msg),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_path(&self) -> Option<&syn::Path> {
|
fn expect_path(&self, msg: &str) -> &syn::Path {
|
||||||
match self {
|
match self {
|
||||||
Meta::Path(path) => Some(path),
|
Meta::Path(path) => path,
|
||||||
_ => None,
|
_ => panic!("{}", msg),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_namevalue(&self) -> Option<&syn::MetaNameValue> {
|
fn expect_namevalue(&self, msg: &str) -> &syn::MetaNameValue {
|
||||||
match self {
|
match self {
|
||||||
Meta::NameValue(pair) => Some(pair),
|
Meta::NameValue(pair) => pair,
|
||||||
_ => None,
|
_ => 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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
@ -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 case_style;
|
||||||
|
pub mod type_props;
|
||||||
|
pub mod variant_props;
|
||||||
|
mod has_metadata;
|
||||||
mod meta_helpers;
|
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()
|
|
||||||
}
|
|
||||||
|
86
strum_macros/src/helpers/type_props.rs
Normal file
86
strum_macros/src/helpers/type_props.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
123
strum_macros/src/helpers/variant_props.rs
Normal file
123
strum_macros/src/helpers/variant_props.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
use proc_macro2::{Span, TokenStream};
|
use proc_macro2::TokenStream;
|
||||||
use syn;
|
use syn;
|
||||||
|
|
||||||
pub(crate) fn enum_count_inner(ast: &syn::DeriveInput) -> TokenStream {
|
pub(crate) fn enum_count_inner(ast: &syn::DeriveInput) -> TokenStream {
|
||||||
@ -9,10 +9,6 @@ pub(crate) fn enum_count_inner(ast: &syn::DeriveInput) -> TokenStream {
|
|||||||
|
|
||||||
// Used in the quasi-quotation below as `#name`
|
// Used in the quasi-quotation below as `#name`
|
||||||
let name = &ast.ident;
|
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
|
// Helper is provided for handling complex generic types correctly and effortlessly
|
||||||
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
|
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
|
||||||
@ -20,12 +16,7 @@ pub(crate) fn enum_count_inner(ast: &syn::DeriveInput) -> TokenStream {
|
|||||||
quote! {
|
quote! {
|
||||||
// Implementation
|
// Implementation
|
||||||
impl #impl_generics ::strum::EnumCount for #name #ty_generics #where_clause {
|
impl #impl_generics ::strum::EnumCount for #name #ty_generics #where_clause {
|
||||||
fn count() -> usize {
|
const COUNT: usize = #n;
|
||||||
#n
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code, missing_docs)]
|
|
||||||
pub const #const_name: usize = #n;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use crate::helpers::{MetaHelpers, MetaIteratorHelpers, MetaListHelpers};
|
|
||||||
use proc_macro2::{Span, TokenStream};
|
use proc_macro2::{Span, TokenStream};
|
||||||
use syn;
|
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.
|
/// 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
|
// Derives for the generated enum
|
||||||
let type_meta = extract_meta(&ast.attrs);
|
let type_properties = ast.get_type_properties();
|
||||||
let discriminant_attrs = type_meta
|
|
||||||
.find_attribute("strum_discriminants")
|
|
||||||
.collect::<Vec<&syn::Meta>>();
|
|
||||||
|
|
||||||
let derives = discriminant_attrs
|
let derives = type_properties.discriminant_derives;
|
||||||
.find_attribute("derive")
|
|
||||||
.map(|meta| meta.path())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let derives = quote! {
|
let derives = quote! {
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, #(#derives),*)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, #(#derives),*)]
|
||||||
@ -40,28 +33,12 @@ pub fn enum_discriminants_inner(ast: &syn::DeriveInput) -> TokenStream {
|
|||||||
Span::call_site(),
|
Span::call_site(),
|
||||||
));
|
));
|
||||||
|
|
||||||
let discriminants_name = discriminant_attrs
|
let discriminants_name = type_properties.discriminant_name.unwrap_or(default_name);
|
||||||
.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);
|
|
||||||
|
|
||||||
// Pass through all other attributes
|
// Pass through all other attributes
|
||||||
let pass_though_attributes = discriminant_attrs
|
let pass_though_attributes = type_properties
|
||||||
.iter()
|
.discriminant_others
|
||||||
.filter(|meta| {
|
.into_iter()
|
||||||
let path = meta.path();
|
|
||||||
!path.is_ident("derive") && !path.is_ident("name")
|
|
||||||
})
|
|
||||||
.map(|meta| quote! { #[ #meta ] })
|
.map(|meta| quote! { #[ #meta ] })
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use syn;
|
use syn;
|
||||||
|
|
||||||
use helpers::{extract_meta, MetaIteratorHelpers};
|
use crate::helpers::HasStrumVariantProperties;
|
||||||
|
|
||||||
pub fn enum_iter_inner(ast: &syn::DeriveInput) -> TokenStream {
|
pub fn enum_iter_inner(ast: &syn::DeriveInput) -> TokenStream {
|
||||||
let name = &ast.ident;
|
let name = &ast.ident;
|
||||||
@ -31,7 +31,7 @@ pub fn enum_iter_inner(ast: &syn::DeriveInput) -> TokenStream {
|
|||||||
let mut arms = Vec::new();
|
let mut arms = Vec::new();
|
||||||
let enabled = variants
|
let enabled = variants
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|variant| !extract_meta(&variant.attrs).is_disabled());
|
.filter(|variant| !variant.get_variant_properties().is_disabled);
|
||||||
|
|
||||||
for (idx, variant) in enabled.enumerate() {
|
for (idx, variant) in enabled.enumerate() {
|
||||||
use syn::Fields::*;
|
use syn::Fields::*;
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use syn;
|
use syn;
|
||||||
|
|
||||||
use crate::helpers::case_style::CaseStyle;
|
use crate::helpers::{HasStrumVariantProperties, HasTypeProperties};
|
||||||
use helpers::{extract_meta, CaseStyleHelpers, MetaIteratorHelpers};
|
|
||||||
|
|
||||||
pub fn enum_message_inner(ast: &syn::DeriveInput) -> TokenStream {
|
pub fn enum_message_inner(ast: &syn::DeriveInput) -> TokenStream {
|
||||||
let name = &ast.ident;
|
let name = &ast.ident;
|
||||||
@ -12,19 +11,16 @@ pub fn enum_message_inner(ast: &syn::DeriveInput) -> TokenStream {
|
|||||||
_ => panic!("EnumMessage only works on Enums"),
|
_ => panic!("EnumMessage only works on Enums"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let type_meta = extract_meta(&ast.attrs);
|
let type_properties = ast.get_type_properties();
|
||||||
let case_style = type_meta
|
|
||||||
.find_unique_property("strum", "serialize_all")
|
|
||||||
.map(|style| CaseStyle::from(style.as_ref()));
|
|
||||||
|
|
||||||
let mut arms = Vec::new();
|
let mut arms = Vec::new();
|
||||||
let mut detailed_arms = Vec::new();
|
let mut detailed_arms = Vec::new();
|
||||||
let mut serializations = Vec::new();
|
let mut serializations = Vec::new();
|
||||||
|
|
||||||
for variant in variants {
|
for variant in variants {
|
||||||
let meta = extract_meta(&variant.attrs);
|
let variant_properties = variant.get_variant_properties();
|
||||||
let messages = meta.find_unique_property("strum", "message");
|
let messages = variant_properties.message.as_ref();
|
||||||
let detailed_messages = meta.find_unique_property("strum", "detailed_message");
|
let detailed_messages = variant_properties.detailed_message.as_ref();
|
||||||
let ident = &variant.ident;
|
let ident = &variant.ident;
|
||||||
|
|
||||||
use syn::Fields::*;
|
use syn::Fields::*;
|
||||||
@ -36,10 +32,7 @@ pub fn enum_message_inner(ast: &syn::DeriveInput) -> TokenStream {
|
|||||||
|
|
||||||
// You can't disable getting the serializations.
|
// You can't disable getting the serializations.
|
||||||
{
|
{
|
||||||
let mut serialization_variants = meta.find_properties("strum", "serialize");
|
let serialization_variants = variant_properties.get_serializations(type_properties.case_style);
|
||||||
if serialization_variants.len() == 0 {
|
|
||||||
serialization_variants.push(ident.convert_case(case_style));
|
|
||||||
}
|
|
||||||
|
|
||||||
let count = serialization_variants.len();
|
let count = serialization_variants.len();
|
||||||
serializations.push(quote! {
|
serializations.push(quote! {
|
||||||
@ -51,7 +44,7 @@ pub fn enum_message_inner(ast: &syn::DeriveInput) -> TokenStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// But you can disable the messages.
|
// But you can disable the messages.
|
||||||
if meta.is_disabled() {
|
if variant_properties.is_disabled {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,28 +1,7 @@
|
|||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use syn;
|
use syn;
|
||||||
use syn::Meta;
|
|
||||||
|
|
||||||
use crate::helpers::{extract_meta, MetaHelpers, MetaIteratorHelpers, MetaListHelpers};
|
use crate::helpers::HasStrumVariantProperties;
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enum_properties_inner(ast: &syn::DeriveInput) -> TokenStream {
|
pub fn enum_properties_inner(ast: &syn::DeriveInput) -> TokenStream {
|
||||||
let name = &ast.ident;
|
let name = &ast.ident;
|
||||||
@ -35,12 +14,12 @@ pub fn enum_properties_inner(ast: &syn::DeriveInput) -> TokenStream {
|
|||||||
let mut arms = Vec::new();
|
let mut arms = Vec::new();
|
||||||
for variant in variants {
|
for variant in variants {
|
||||||
let ident = &variant.ident;
|
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 string_arms = Vec::new();
|
||||||
let mut bool_arms = Vec::new();
|
let mut bool_arms = Vec::new();
|
||||||
let mut num_arms = Vec::new();
|
let mut num_arms = Vec::new();
|
||||||
// But you can disable the messages.
|
// But you can disable the messages.
|
||||||
if meta.is_disabled() {
|
if variant_properties.is_disabled {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,17 +30,8 @@ pub fn enum_properties_inner(ast: &syn::DeriveInput) -> TokenStream {
|
|||||||
Named(..) => quote! { {..} },
|
Named(..) => quote! { {..} },
|
||||||
};
|
};
|
||||||
|
|
||||||
for (key, value) in extract_properties(&meta) {
|
for (key, value) in variant_properties.string_props {
|
||||||
use syn::Lit::*;
|
string_arms.push(quote! { #key => ::std::option::Option::Some( #value )})
|
||||||
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 )}),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string_arms.push(quote! { _ => ::std::option::Option::None });
|
string_arms.push(quote! { _ => ::std::option::Option::None });
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use syn;
|
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 {
|
pub fn enum_variant_names_inner(ast: &syn::DeriveInput) -> TokenStream {
|
||||||
let name = &ast.ident;
|
let name = &ast.ident;
|
||||||
@ -14,14 +14,11 @@ pub fn enum_variant_names_inner(ast: &syn::DeriveInput) -> TokenStream {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Derives for the generated enum
|
// Derives for the generated enum
|
||||||
let type_meta = extract_meta(&ast.attrs);
|
let type_properties = ast.get_type_properties();
|
||||||
let case_style = type_meta
|
|
||||||
.find_unique_property("strum", "serialize_all")
|
|
||||||
.map(|style| CaseStyle::from(style.as_ref()));
|
|
||||||
|
|
||||||
let names = variants
|
let names = variants
|
||||||
.iter()
|
.iter()
|
||||||
.map(|v| v.ident.convert_case(case_style))
|
.map(|v| v.ident.convert_case(type_properties.case_style))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use syn;
|
use syn;
|
||||||
|
|
||||||
use crate::helpers::case_style::CaseStyle;
|
use crate::helpers::{HasTypeProperties, HasStrumVariantProperties};
|
||||||
use helpers::{extract_meta, CaseStyleHelpers, MetaIteratorHelpers};
|
|
||||||
|
|
||||||
fn get_arms(ast: &syn::DeriveInput) -> Vec<TokenStream> {
|
fn get_arms(ast: &syn::DeriveInput) -> Vec<TokenStream> {
|
||||||
let name = &ast.ident;
|
let name = &ast.ident;
|
||||||
@ -12,36 +11,21 @@ fn get_arms(ast: &syn::DeriveInput) -> Vec<TokenStream> {
|
|||||||
_ => panic!("This macro only works on Enums"),
|
_ => panic!("This macro only works on Enums"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let type_meta = extract_meta(&ast.attrs);
|
let type_properties = ast.get_type_properties();
|
||||||
let case_style = type_meta
|
|
||||||
.find_unique_property("strum", "serialize_all")
|
|
||||||
.map(|style| CaseStyle::from(style.as_ref()));
|
|
||||||
|
|
||||||
for variant in variants {
|
for variant in variants {
|
||||||
use syn::Fields::*;
|
use syn::Fields::*;
|
||||||
let ident = &variant.ident;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look at all the serialize attributes.
|
// Look at all the serialize attributes.
|
||||||
// Use `to_string` attribute (not `as_ref_str` or something) to keep things consistent
|
// 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()`).
|
// (i.e. always `enum.as_ref().to_string() == enum.to_string()`).
|
||||||
let output = if let Some(n) = meta.find_unique_property("strum", "to_string") {
|
let output = variant_properties.get_preferred_name(type_properties.case_style);
|
||||||
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 params = match variant.fields {
|
let params = match variant.fields {
|
||||||
Unit => quote! {},
|
Unit => quote! {},
|
||||||
Unnamed(..) => quote! { (..) },
|
Unnamed(..) => quote! { (..) },
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use syn;
|
use syn;
|
||||||
|
|
||||||
use crate::helpers::case_style::CaseStyle;
|
use crate::helpers::{HasTypeProperties, HasStrumVariantProperties};
|
||||||
use helpers::{extract_meta, CaseStyleHelpers, MetaIteratorHelpers};
|
|
||||||
|
|
||||||
pub fn display_inner(ast: &syn::DeriveInput) -> TokenStream {
|
pub fn display_inner(ast: &syn::DeriveInput) -> TokenStream {
|
||||||
let name = &ast.ident;
|
let name = &ast.ident;
|
||||||
@ -12,39 +11,24 @@ pub fn display_inner(ast: &syn::DeriveInput) -> TokenStream {
|
|||||||
_ => panic!("Display only works on Enums"),
|
_ => panic!("Display only works on Enums"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let type_meta = extract_meta(&ast.attrs);
|
let type_properties = ast.get_type_properties();
|
||||||
let case_style = type_meta
|
|
||||||
.find_unique_property("strum", "serialize_all")
|
|
||||||
.map(|style| CaseStyle::from(style.as_ref()));
|
|
||||||
|
|
||||||
let mut arms = Vec::new();
|
let mut arms = Vec::new();
|
||||||
for variant in variants {
|
for variant in variants {
|
||||||
use syn::Fields::*;
|
|
||||||
let ident = &variant.ident;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look at all the serialize attributes.
|
// Look at all the serialize attributes.
|
||||||
let output = if let Some(n) = meta.find_unique_property("strum", "to_string") {
|
let output = variant_properties.get_preferred_name(type_properties.case_style);
|
||||||
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 params = match variant.fields {
|
let params = match variant.fields {
|
||||||
Unit => quote! {},
|
syn::Fields::Unit => quote! {},
|
||||||
Unnamed(..) => quote! { (..) },
|
syn::Fields::Unnamed(..) => quote! { (..) },
|
||||||
Named(..) => quote! { {..} },
|
syn::Fields::Named(..) => quote! { {..} },
|
||||||
};
|
};
|
||||||
|
|
||||||
arms.push(quote! { #name::#ident #params => f.pad(#output) });
|
arms.push(quote! { #name::#ident #params => f.pad(#output) });
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use syn;
|
use syn;
|
||||||
|
|
||||||
use crate::helpers::case_style::CaseStyle;
|
use crate::helpers::{HasTypeProperties, HasStrumVariantProperties};
|
||||||
use crate::helpers::{extract_meta, CaseStyleHelpers, MetaIteratorHelpers};
|
|
||||||
|
|
||||||
pub fn from_string_inner(ast: &syn::DeriveInput) -> TokenStream {
|
pub fn from_string_inner(ast: &syn::DeriveInput) -> TokenStream {
|
||||||
let name = &ast.ident;
|
let name = &ast.ident;
|
||||||
@ -12,10 +11,7 @@ pub fn from_string_inner(ast: &syn::DeriveInput) -> TokenStream {
|
|||||||
_ => panic!("FromString only works on Enums"),
|
_ => panic!("FromString only works on Enums"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let type_meta = extract_meta(&ast.attrs);
|
let type_properties = ast.get_type_properties();
|
||||||
let case_style = type_meta
|
|
||||||
.find_unique_property("strum", "serialize_all")
|
|
||||||
.map(|style| CaseStyle::from(style.as_ref()));
|
|
||||||
|
|
||||||
let mut has_default = false;
|
let mut has_default = false;
|
||||||
let mut default =
|
let mut default =
|
||||||
@ -24,24 +20,13 @@ pub fn from_string_inner(ast: &syn::DeriveInput) -> TokenStream {
|
|||||||
for variant in variants {
|
for variant in variants {
|
||||||
use syn::Fields::*;
|
use syn::Fields::*;
|
||||||
let ident = &variant.ident;
|
let ident = &variant.ident;
|
||||||
let meta = extract_meta(&variant.attrs);
|
let variant_properties = variant.get_variant_properties();
|
||||||
|
|
||||||
// Look at all the serialize attributes.
|
if variant_properties.is_disabled {
|
||||||
// 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() {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if meta
|
if variant_properties.default {
|
||||||
.find_unique_property("strum", "default")
|
|
||||||
.map_or(false, |s| s == "true")
|
|
||||||
{
|
|
||||||
if has_default {
|
if has_default {
|
||||||
panic!("Can't have multiple default variants");
|
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 we don't have any custom variants, add the default serialized name.
|
||||||
if attrs.len() == 0 {
|
let attrs = variant_properties.get_serializations(type_properties.case_style);
|
||||||
attrs.push(ident.convert_case(case_style));
|
|
||||||
}
|
|
||||||
|
|
||||||
let params = match variant.fields {
|
let params = match variant.fields {
|
||||||
Unit => quote! {},
|
Unit => quote! {},
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use syn;
|
use syn;
|
||||||
|
|
||||||
use crate::helpers::case_style::CaseStyle;
|
use crate::helpers::{HasStrumVariantProperties, HasTypeProperties};
|
||||||
use crate::helpers::{extract_meta, CaseStyleHelpers, MetaIteratorHelpers};
|
|
||||||
|
|
||||||
pub fn to_string_inner(ast: &syn::DeriveInput) -> TokenStream {
|
pub fn to_string_inner(ast: &syn::DeriveInput) -> TokenStream {
|
||||||
let name = &ast.ident;
|
let name = &ast.ident;
|
||||||
@ -12,34 +11,19 @@ pub fn to_string_inner(ast: &syn::DeriveInput) -> TokenStream {
|
|||||||
_ => panic!("ToString only works on Enums"),
|
_ => panic!("ToString only works on Enums"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let type_meta = extract_meta(&ast.attrs);
|
let type_properties = ast.get_type_properties();
|
||||||
let case_style = type_meta
|
|
||||||
.find_unique_property("strum", "serialize_all")
|
|
||||||
.map(|style| CaseStyle::from(style.as_ref()));
|
|
||||||
|
|
||||||
let mut arms = Vec::new();
|
let mut arms = Vec::new();
|
||||||
for variant in variants {
|
for variant in variants {
|
||||||
use syn::Fields::*;
|
use syn::Fields::*;
|
||||||
let ident = &variant.ident;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look at all the serialize attributes.
|
// Look at all the serialize attributes.
|
||||||
let output = if let Some(n) = meta.find_unique_property("strum", "to_string") {
|
let output = variant_properties.get_preferred_name(type_properties.case_style);
|
||||||
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 params = match variant.fields {
|
let params = match variant.fields {
|
||||||
Unit => quote! {},
|
Unit => quote! {},
|
||||||
|
@ -14,6 +14,6 @@ pub enum Color {
|
|||||||
Blue { hue: usize },
|
Blue { hue: usize },
|
||||||
#[strum(serialize = "y", serialize = "yellow")]
|
#[strum(serialize = "y", serialize = "yellow")]
|
||||||
Yellow,
|
Yellow,
|
||||||
#[strum(disabled = "true")]
|
#[strum(disabled)]
|
||||||
Green(String),
|
Green(String),
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ enum Color {
|
|||||||
Blue { hue: usize },
|
Blue { hue: usize },
|
||||||
#[strum(serialize = "y", serialize = "yellow")]
|
#[strum(serialize = "y", serialize = "yellow")]
|
||||||
Yellow,
|
Yellow,
|
||||||
#[strum(disabled = "true")]
|
#[strum(disabled)]
|
||||||
Green(String),
|
Green(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ enum Color {
|
|||||||
Blue { hue: usize },
|
Blue { hue: usize },
|
||||||
#[strum(serialize = "y", serialize = "yellow")]
|
#[strum(serialize = "y", serialize = "yellow")]
|
||||||
Yellow,
|
Yellow,
|
||||||
#[strum(default = "true")]
|
#[strum(default)]
|
||||||
Green(String),
|
Green(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ enum Color {
|
|||||||
Blue { hue: usize },
|
Blue { hue: usize },
|
||||||
#[strum(serialize = "y", serialize = "yellow")]
|
#[strum(serialize = "y", serialize = "yellow")]
|
||||||
Yellow,
|
Yellow,
|
||||||
#[strum(default = "true")]
|
#[strum(default)]
|
||||||
Green(String),
|
Green(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ enum Color {
|
|||||||
Blue { hue: usize },
|
Blue { hue: usize },
|
||||||
#[strum(serialize = "y", serialize = "yellow")]
|
#[strum(serialize = "y", serialize = "yellow")]
|
||||||
Yellow,
|
Yellow,
|
||||||
#[strum(default = "true")]
|
#[strum(default)]
|
||||||
Green(String),
|
Green(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,9 +21,18 @@ fn to_blue_string() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_formatters() {
|
fn test_formatters() {
|
||||||
assert_eq!(String::from(" blue"), format!("{:>6}", Color::Blue { hue: 0 }));
|
assert_eq!(
|
||||||
assert_eq!(String::from("blue "), format!("{:<6}", Color::Blue { hue: 0 }));
|
String::from(" blue"),
|
||||||
assert_eq!(String::from(" blue "), format!("{:^6}", Color::Blue { hue: 0 }));
|
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 }));
|
assert_eq!(String::from("bl"), format!("{:.2}", Color::Blue { hue: 0 }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ enum Week {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn simple_test() {
|
fn simple_test() {
|
||||||
assert_eq!(7, Week::count());
|
assert_eq!(7, Week::COUNT);
|
||||||
assert_eq!(Week::count(), WEEK_COUNT);
|
assert_eq!(Week::iter().count(), Week::COUNT);
|
||||||
assert_eq!(Week::iter().count(), WEEK_COUNT);
|
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ enum Pets {
|
|||||||
#[strum(detailed_message = "My fish is named Charles McFish")]
|
#[strum(detailed_message = "My fish is named Charles McFish")]
|
||||||
Fish,
|
Fish,
|
||||||
Bird,
|
Bird,
|
||||||
#[strum(disabled = "true")]
|
#[strum(disabled)]
|
||||||
Hamster,
|
Hamster,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ enum Color {
|
|||||||
},
|
},
|
||||||
#[strum(serialize = "y", serialize = "yellow")]
|
#[strum(serialize = "y", serialize = "yellow")]
|
||||||
Yellow,
|
Yellow,
|
||||||
#[strum(default = "true")]
|
#[strum(default)]
|
||||||
Green(String),
|
Green(String),
|
||||||
#[strum(to_string = "purp")]
|
#[strum(to_string = "purp")]
|
||||||
Purple,
|
Purple,
|
||||||
|
@ -13,7 +13,7 @@ enum Color {
|
|||||||
Blue { hue: usize },
|
Blue { hue: usize },
|
||||||
#[strum(serialize = "y", serialize = "yellow")]
|
#[strum(serialize = "y", serialize = "yellow")]
|
||||||
Yellow,
|
Yellow,
|
||||||
#[strum(default = "true")]
|
#[strum(default)]
|
||||||
Green(String),
|
Green(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user