Implement container-level case_insensitive from issue #1
This commit is contained in:
1 parent
d0da42b9b7
commit
8c078040ac
3 files changed
+105
-23
No files matched your search
@@ -3,11 +3,11 @@ use std::collections::HashSet;
|
|||||||
use proc_macro2::{Ident, Span};
|
use proc_macro2::{Ident, Span};
|
||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens};
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
use syn::{DataEnum, Fields};
|
use syn::{DataEnum, Fields, Attribute};
|
||||||
|
|
||||||
use crate::attribute::{Dict, Value};
|
use crate::attribute::{Dict, Value};
|
||||||
use crate::error::{MacroError, MacroResult};
|
use crate::error::{MacroError, MacroResult};
|
||||||
use crate::TokenStream2;
|
use crate::{TokenStream2, CASE_SENSITIVE};
|
||||||
use crate::{CASE_INSENSITIVE, CRATE_ATTR, IGNORE, NAME, OTHER};
|
use crate::{CASE_INSENSITIVE, CRATE_ATTR, IGNORE, NAME, OTHER};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -94,13 +94,27 @@ impl VariantConstructor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_enum(data: &DataEnum) -> MacroResult<Enum> {
|
pub(crate) fn parse_enum<'a>(data: &'a DataEnum, attrs: &'a [Attribute]) -> MacroResult<Enum<'a>> {
|
||||||
let mut variants = Vec::with_capacity(data.variants.len());
|
let mut variants = Vec::with_capacity(data.variants.len());
|
||||||
let mut taken_names = HashSet::new();
|
let mut taken_names = HashSet::new();
|
||||||
let mut taken_insensitive_names = HashSet::new();
|
let mut taken_insensitive_names = HashSet::new();
|
||||||
let mut taken_sensitive_names = HashSet::new();
|
let mut taken_sensitive_names = HashSet::new();
|
||||||
let mut other_variant = false;
|
let mut other_variant = false;
|
||||||
|
|
||||||
|
let global_case_insensitive = {
|
||||||
|
let mut dict = Dict::from_attrs(CRATE_ATTR, attrs)?;
|
||||||
|
|
||||||
|
let (global_case_insensitive, _) = dict.remove_typed_or_default(
|
||||||
|
CASE_INSENSITIVE,
|
||||||
|
(false, data.enum_token.span()),
|
||||||
|
Value::value_bool,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
dict.assert_empty()?;
|
||||||
|
|
||||||
|
global_case_insensitive
|
||||||
|
};
|
||||||
|
|
||||||
for variant in data.variants.iter() {
|
for variant in data.variants.iter() {
|
||||||
let variant_span = variant.span();
|
let variant_span = variant.span();
|
||||||
|
|
||||||
@@ -108,17 +122,50 @@ pub(crate) fn parse_enum(data: &DataEnum) -> MacroResult<Enum> {
|
|||||||
let mut dict = Dict::from_attrs(CRATE_ATTR, &variant.attrs)?;
|
let mut dict = Dict::from_attrs(CRATE_ATTR, &variant.attrs)?;
|
||||||
|
|
||||||
// Convert the values in the Dict to the appropriate types
|
// Convert the values in the Dict to the appropriate types
|
||||||
let name_opt = dict.remove_typed(NAME, Value::value_string)?;
|
let name_opt = dict.remove_typed(
|
||||||
let (other, other_span) =
|
NAME,
|
||||||
dict.remove_typed_or_default(OTHER, (false, variant_span), Value::value_bool)?;
|
Value::value_string
|
||||||
let (ignore, _) =
|
)?;
|
||||||
dict.remove_typed_or_default(IGNORE, (false, variant_span), Value::value_bool)?;
|
|
||||||
|
let (other, other_span) = dict.remove_typed_or_default(
|
||||||
|
OTHER,
|
||||||
|
(false, variant_span),
|
||||||
|
Value::value_bool
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let (ignore, _) = dict.remove_typed_or_default(
|
||||||
|
IGNORE,
|
||||||
|
(false, variant_span),
|
||||||
|
Value::value_bool
|
||||||
|
)?;
|
||||||
|
|
||||||
let (case_insensitive, _) = dict.remove_typed_or_default(
|
let (case_insensitive, _) = dict.remove_typed_or_default(
|
||||||
CASE_INSENSITIVE,
|
CASE_INSENSITIVE,
|
||||||
(false, variant_span),
|
(false, variant_span),
|
||||||
Value::value_bool,
|
Value::value_bool,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let (case_sensitive, case_sensitive_span) = dict.remove_typed_or_default(
|
||||||
|
CASE_SENSITIVE,
|
||||||
|
(false, variant_span),
|
||||||
|
Value::value_bool
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let case_insensitive = match (case_insensitive, case_sensitive) {
|
||||||
|
(false, false) => global_case_insensitive,
|
||||||
|
(false, true) => false,
|
||||||
|
(true, false) => true,
|
||||||
|
(true, true) => {
|
||||||
|
return Err(MacroError::new(
|
||||||
|
format!(
|
||||||
|
"variant {} cannot be both case_insensitive and case_sensitive",
|
||||||
|
variant.ident,
|
||||||
|
),
|
||||||
|
case_sensitive_span,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Return an error if there are any unrecognised keys in the Dict
|
// Return an error if there are any unrecognised keys in the Dict
|
||||||
dict.assert_empty()?;
|
dict.assert_empty()?;
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use proc_macro::TokenStream;
|
|||||||
|
|
||||||
use proc_macro2::Ident;
|
use proc_macro2::Ident;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{Data, DataEnum, DeriveInput};
|
use syn::{Data, DataEnum, DeriveInput, Attribute};
|
||||||
|
|
||||||
use error::{MacroError, MacroResult};
|
use error::{MacroError, MacroResult};
|
||||||
|
|
||||||
@@ -26,6 +26,7 @@ const NAME: &'static str = "str";
|
|||||||
const OTHER: &'static str = "other";
|
const OTHER: &'static str = "other";
|
||||||
const IGNORE: &'static str = "ignore";
|
const IGNORE: &'static str = "ignore";
|
||||||
const CASE_INSENSITIVE: &'static str = "case_insensitive";
|
const CASE_INSENSITIVE: &'static str = "case_insensitive";
|
||||||
|
const CASE_SENSITIVE: &'static str = "case_sensitive";
|
||||||
|
|
||||||
type TokenStream2 = proc_macro2::TokenStream;
|
type TokenStream2 = proc_macro2::TokenStream;
|
||||||
|
|
||||||
@@ -53,8 +54,8 @@ where
|
|||||||
{
|
{
|
||||||
let input: DeriveInput = syn::parse(input).expect("failed to parse input");
|
let input: DeriveInput = syn::parse(input).expect("failed to parse input");
|
||||||
|
|
||||||
let enum_data = proc_try!(get_enum_data(&input));
|
let (enum_data, enum_attrs) = proc_try!(get_enum_data(&input));
|
||||||
let parsed_enum = proc_try!(enums::parse_enum(enum_data));
|
let parsed_enum = proc_try!(enums::parse_enum(enum_data, enum_attrs));
|
||||||
|
|
||||||
let enum_ident = &input.ident;
|
let enum_ident = &input.ident;
|
||||||
|
|
||||||
@@ -95,8 +96,8 @@ where
|
|||||||
{
|
{
|
||||||
let input: DeriveInput = syn::parse(input).expect("failed to parse input");
|
let input: DeriveInput = syn::parse(input).expect("failed to parse input");
|
||||||
|
|
||||||
let enum_data = proc_try!(get_enum_data(&input));
|
let (enum_data, enum_attrs) = proc_try!(get_enum_data(&input));
|
||||||
let parsed_enum = proc_try!(enums::parse_enum(enum_data));
|
let parsed_enum = proc_try!(enums::parse_enum(enum_data, enum_attrs));
|
||||||
|
|
||||||
let enum_ident = &input.ident;
|
let enum_ident = &input.ident;
|
||||||
|
|
||||||
@@ -147,8 +148,8 @@ where
|
|||||||
{
|
{
|
||||||
let input: DeriveInput = syn::parse(input).expect("failed to parse input");
|
let input: DeriveInput = syn::parse(input).expect("failed to parse input");
|
||||||
|
|
||||||
let enum_data = proc_try!(get_enum_data(&input));
|
let (enum_data, enum_attrs) = proc_try!(get_enum_data(&input));
|
||||||
let parsed_enum = proc_try!(enums::parse_enum(enum_data));
|
let parsed_enum = proc_try!(enums::parse_enum(enum_data, enum_attrs));
|
||||||
|
|
||||||
let enum_ident = &input.ident;
|
let enum_ident = &input.ident;
|
||||||
|
|
||||||
@@ -645,8 +646,8 @@ pub fn derive_try_unscribe(input: TokenStream) -> TokenStream {
|
|||||||
pub fn derive_enum_serialize(input: TokenStream) -> TokenStream {
|
pub fn derive_enum_serialize(input: TokenStream) -> TokenStream {
|
||||||
let input: DeriveInput = syn::parse(input).expect("failed to parse input");
|
let input: DeriveInput = syn::parse(input).expect("failed to parse input");
|
||||||
|
|
||||||
let enum_data = proc_try!(get_enum_data(&input));
|
let (enum_data, enum_attrs) = proc_try!(get_enum_data(&input));
|
||||||
let parsed_enum = proc_try!(enums::parse_enum(enum_data));
|
let parsed_enum = proc_try!(enums::parse_enum(enum_data, enum_attrs));
|
||||||
|
|
||||||
let enum_ident = &input.ident;
|
let enum_ident = &input.ident;
|
||||||
let serializer_ident = quote! { __enumscribe_serializer };
|
let serializer_ident = quote! { __enumscribe_serializer };
|
||||||
@@ -737,8 +738,8 @@ pub fn derive_enum_serialize(input: TokenStream) -> TokenStream {
|
|||||||
pub fn derive_enum_deserialize(input: TokenStream) -> TokenStream {
|
pub fn derive_enum_deserialize(input: TokenStream) -> TokenStream {
|
||||||
let input: DeriveInput = syn::parse(input).expect("failed to parse input");
|
let input: DeriveInput = syn::parse(input).expect("failed to parse input");
|
||||||
|
|
||||||
let enum_data = proc_try!(get_enum_data(&input));
|
let (enum_data, enum_attrs) = proc_try!(get_enum_data(&input));
|
||||||
let parsed_enum = proc_try!(enums::parse_enum(enum_data));
|
let parsed_enum = proc_try!(enums::parse_enum(enum_data, enum_attrs));
|
||||||
|
|
||||||
let enum_ident = &input.ident;
|
let enum_ident = &input.ident;
|
||||||
|
|
||||||
@@ -792,7 +793,7 @@ pub fn derive_enum_deserialize(input: TokenStream) -> TokenStream {
|
|||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_enum_data(input: &DeriveInput) -> MacroResult<&DataEnum> {
|
fn get_enum_data(input: &DeriveInput) -> MacroResult<(&DataEnum, &[Attribute])> {
|
||||||
let enum_data = match &input.data {
|
let enum_data = match &input.data {
|
||||||
Data::Enum(enum_data) => enum_data,
|
Data::Enum(enum_data) => enum_data,
|
||||||
Data::Struct(_) => {
|
Data::Struct(_) => {
|
||||||
@@ -816,5 +817,5 @@ fn get_enum_data(input: &DeriveInput) -> MacroResult<&DataEnum> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(enum_data)
|
Ok((enum_data, &input.attrs))
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,7 @@ fn test_unscribe() {
|
|||||||
#[enumscribe(str = "foo")]
|
#[enumscribe(str = "foo")]
|
||||||
V1,
|
V1,
|
||||||
V2(),
|
V2(),
|
||||||
#[enumscribe(str = "BAA")]
|
#[enumscribe(str = "BAA", case_sensitive)]
|
||||||
V3(),
|
V3(),
|
||||||
V4 {},
|
V4 {},
|
||||||
#[enumscribe(str = "BaZ")]
|
#[enumscribe(str = "BaZ")]
|
||||||
@@ -100,6 +100,24 @@ fn test_unscribe() {
|
|||||||
assert_eq!(E0::unscribe("dolorr"), E0::V12("dolorr".to_owned()));
|
assert_eq!(E0::unscribe("dolorr"), E0::V12("dolorr".to_owned()));
|
||||||
assert_eq!(E0::unscribe(""), E0::V12("".to_owned()));
|
assert_eq!(E0::unscribe(""), E0::V12("".to_owned()));
|
||||||
assert_eq!(E0::unscribe("\0"), E0::V12("\0".to_owned()));
|
assert_eq!(E0::unscribe("\0"), E0::V12("\0".to_owned()));
|
||||||
|
|
||||||
|
#[derive(Unscribe, Eq, PartialEq, Debug)]
|
||||||
|
#[enumscribe(case_insensitive)]
|
||||||
|
enum E1 {
|
||||||
|
#[enumscribe(str = "foo")]
|
||||||
|
V0,
|
||||||
|
#[enumscribe(str = "baa", case_sensitive)]
|
||||||
|
V1,
|
||||||
|
#[enumscribe(other)]
|
||||||
|
V2(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(E1::unscribe("foo"), E1::V0);
|
||||||
|
assert_eq!(E1::unscribe("Foo"), E1::V0);
|
||||||
|
assert_eq!(E1::unscribe("FOO"), E1::V0);
|
||||||
|
assert_eq!(E1::unscribe("baa"), E1::V1);
|
||||||
|
assert_eq!(E1::unscribe("Baa"), E1::V2("Baa".to_owned()));
|
||||||
|
assert_eq!(E1::unscribe("BAA"), E1::V2("BAA".to_owned()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -110,7 +128,7 @@ fn test_try_unscribe() {
|
|||||||
#[enumscribe(str = "foo")]
|
#[enumscribe(str = "foo")]
|
||||||
V1,
|
V1,
|
||||||
V2(),
|
V2(),
|
||||||
#[enumscribe(str = "BAA")]
|
#[enumscribe(str = "BAA", case_sensitive)]
|
||||||
V3(),
|
V3(),
|
||||||
V4 {},
|
V4 {},
|
||||||
#[enumscribe(str = "BaZ")]
|
#[enumscribe(str = "BaZ")]
|
||||||
@@ -317,4 +335,20 @@ fn test_try_unscribe() {
|
|||||||
);
|
);
|
||||||
assert_eq!(E1::try_unscribe(""), Some(E1::V12("".to_owned())));
|
assert_eq!(E1::try_unscribe(""), Some(E1::V12("".to_owned())));
|
||||||
assert_eq!(E1::try_unscribe("\0"), Some(E1::V12("\0".to_owned())));
|
assert_eq!(E1::try_unscribe("\0"), Some(E1::V12("\0".to_owned())));
|
||||||
|
|
||||||
|
#[derive(TryUnscribe, Eq, PartialEq, Debug)]
|
||||||
|
#[enumscribe(case_insensitive)]
|
||||||
|
enum E2 {
|
||||||
|
#[enumscribe(str = "foo")]
|
||||||
|
V0,
|
||||||
|
#[enumscribe(str = "baa", case_sensitive)]
|
||||||
|
V1,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(E2::try_unscribe("foo"), Some(E2::V0));
|
||||||
|
assert_eq!(E2::try_unscribe("Foo"), Some(E2::V0));
|
||||||
|
assert_eq!(E2::try_unscribe("FOO"), Some(E2::V0));
|
||||||
|
assert_eq!(E2::try_unscribe("baa"), Some(E2::V1));
|
||||||
|
assert_eq!(E2::try_unscribe("Baa"), None);
|
||||||
|
assert_eq!(E2::try_unscribe("BAA"), None);
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user