Run cargo fmt, minor code cleanup

rename
Pantonshire 5 years ago
parent 4317afa003
commit f9a9db2e9a

@ -2,9 +2,9 @@ use std::collections::HashMap;
use std::fmt;
use proc_macro2::Span;
use syn::{Attribute, Ident, Lit, Token};
use syn::parse::{Parse, ParseBuffer, ParseStream};
use syn::parse::discouraged::Speculative;
use syn::parse::{Parse, ParseBuffer, ParseStream};
use syn::{Attribute, Ident, Lit, Token};
use crate::error::{MacroError, MacroResult, ValueTypeError, ValueTypeResult};
@ -40,8 +40,8 @@ impl Value {
Value::None => Ok(true),
Value::Lit(Lit::Bool(lit_bool)) => Ok(lit_bool.value),
val => Err(ValueTypeError {
message: format!("expected boolean but found {}", val.type_name()).into()
})
message: format!("expected boolean but found {}", val.type_name()).into(),
}),
}
}
@ -51,8 +51,8 @@ impl Value {
match self {
Value::Lit(Lit::Str(lit_str)) => Ok(lit_str.value()),
val => Err(ValueTypeError {
message: format!("expected string but found {}", val.type_name()).into()
})
message: format!("expected string but found {}", val.type_name()).into(),
}),
}
}
}
@ -97,13 +97,16 @@ struct KeyValPair {
impl Dict {
pub(crate) fn new() -> Self {
Dict { inner: HashMap::new() }
Dict {
inner: HashMap::new(),
}
}
pub(crate) fn from_attrs(name: &str, attrs: &[Attribute]) -> MacroResult<Self> {
let mut dict = Dict::new();
let attribute_tags = attrs.iter()
let attribute_tags = attrs
.iter()
.filter(|attr| attr.path.is_ident(name))
.map(|attr| attr.parse_args::<AttributeTag>());
@ -112,9 +115,10 @@ impl Dict {
for (key, val, span) in tag.inner {
if dict.inner.contains_key(&key) {
return Err(MacroError::new(format!(
"key appears more than once: {}", key
), span));
return Err(MacroError::new(
format!("key appears more than once: {}", key),
span,
));
}
dict.inner.insert(key, (val, span));
@ -124,9 +128,13 @@ impl Dict {
Ok(dict)
}
pub(crate) fn remove_typed<T, F>(&mut self, key: &str, converter: F) -> MacroResult<Option<(T, Span)>>
where
F: Fn(&Value) -> ValueTypeResult<T>
pub(crate) fn remove_typed<T, F>(
&mut self,
key: &str,
converter: F,
) -> MacroResult<Option<(T, Span)>>
where
F: Fn(&Value) -> ValueTypeResult<T>,
{
match self.inner.remove(key) {
None => Ok(None),
@ -135,14 +143,19 @@ impl Dict {
Err(ValueTypeError { message }) => Err(MacroError::new(
format!("{} for key: {}", message, key),
span,
))
}
)),
},
}
}
pub(crate) fn remove_typed_or_default<T, F>(&mut self, key: &str, default: (T, Span), converter: F) -> MacroResult<(T, Span)>
where
F: Fn(&Value) -> ValueTypeResult<T>
pub(crate) fn remove_typed_or_default<T, F>(
&mut self,
key: &str,
default: (T, Span),
converter: F,
) -> MacroResult<(T, Span)>
where
F: Fn(&Value) -> ValueTypeResult<T>,
{
match self.remove_typed(key, converter) {
Ok(Some(value)) => Ok(value),
@ -171,15 +184,14 @@ impl Parse for AttributeTag {
.parse_terminated::<KeyValPair, Token![,]>(KeyValPair::parse)?
.into_iter()
.map(|pair| (pair.key, pair.val, pair.span))
.collect::<Vec<_>>()
.collect::<Vec<_>>(),
})
}
}
impl Parse for KeyValPair {
fn parse(input: ParseStream) -> syn::Result<Self> {
let key = input
.parse::<Ident>()?;
let key = input.parse::<Ident>()?;
let val = if input.peek(Token![=]) {
input.parse::<Token![=]>()?;
@ -190,7 +202,8 @@ impl Parse for KeyValPair {
Value::Ident(ident)
} else {
return Err(input.error(format!(
"could not parse value corresponding to key: {}", key
"could not parse value corresponding to key: {}",
key
)));
}
} else {
@ -205,17 +218,23 @@ impl Parse for KeyValPair {
}
}
fn speculative_parse<T>(input: ParseStream) -> syn::Result<T> where T: Parse {
fn speculative_parse<T>(input: ParseStream) -> syn::Result<T>
where
T: Parse,
{
match fork_and_parse(input) {
Ok((fork, parsed)) => {
input.advance_to(&fork);
Ok(parsed)
}
Err(err) => Err(err)
Err(err) => Err(err),
}
}
fn fork_and_parse<T>(input: ParseStream) -> syn::Result<(ParseBuffer, T)> where T: Parse {
fn fork_and_parse<T>(input: ParseStream) -> syn::Result<(ParseBuffer, T)>
where
T: Parse,
{
let fork = input.fork();
T::parse(&fork).map(move |parsed| (fork, parsed))
}

@ -2,13 +2,13 @@ use std::collections::HashSet;
use proc_macro2::{Ident, Span};
use quote::{quote, ToTokens};
use syn::{DataEnum, Fields};
use syn::spanned::Spanned;
use syn::{DataEnum, Fields};
use crate::{CASE_INSENSITIVE, CRATE_ATTR, IGNORE, NAME, OTHER};
use crate::TokenStream2;
use crate::attribute::{Dict, Value};
use crate::error::{MacroError, MacroResult};
use crate::TokenStream2;
use crate::{CASE_INSENSITIVE, CRATE_ATTR, IGNORE, NAME, OTHER};
#[derive(Clone)]
pub(crate) struct Enum<'a> {
@ -31,7 +31,7 @@ pub(crate) enum VariantType<'a> {
case_insensitive: bool,
},
Other {
field_name: Option<&'a Ident>
field_name: Option<&'a Ident>,
},
}
@ -47,18 +47,20 @@ impl<'a> Variant<'a> {
&self,
enum_ident: &Ident,
named_fn: &F,
other_fn: &G
other_fn: &G,
) -> MacroResult<Option<(TokenStream2, TokenStream2)>>
where
F: Fn(&Variant, &Ident, &str) -> MacroResult<TokenStream2>,
G: Fn(&Variant, &Ident, TokenStream2) -> MacroResult<TokenStream2>
where
F: Fn(&Variant, &Ident, &str) -> MacroResult<TokenStream2>,
G: Fn(&Variant, &Ident, TokenStream2) -> MacroResult<TokenStream2>,
{
let variant_ident = &self.data.ident;
match &self.v_type {
VariantType::Ignore => Ok(None),
VariantType::Named { name, constructor, .. } => {
VariantType::Named {
name, constructor, ..
} => {
let constructor_tokens = constructor.empty();
let pattern = quote! { #enum_ident::#variant_ident #constructor_tokens };
Ok(Some((pattern, named_fn(self, enum_ident, name)?)))
@ -67,13 +69,16 @@ impl<'a> Variant<'a> {
VariantType::Other { field_name } => {
let field_name_tokens = match field_name {
Some(field_name) => field_name.to_token_stream(),
None => quote! { __enumscribe_other_inner }
None => quote! { __enumscribe_other_inner },
};
let pattern = match field_name {
Some(_) => quote! { #enum_ident::#variant_ident{#field_name_tokens} },
None => quote! { #enum_ident::#variant_ident(#field_name_tokens) }
None => quote! { #enum_ident::#variant_ident(#field_name_tokens) },
};
Ok(Some((pattern, other_fn(self, enum_ident, field_name_tokens)?)))
Ok(Some((
pattern,
other_fn(self, enum_ident, field_name_tokens)?,
)))
}
}
}
@ -84,7 +89,7 @@ impl VariantConstructor {
match self {
VariantConstructor::None => quote! {},
VariantConstructor::Paren => quote! { () },
VariantConstructor::Brace => quote! { {} }
VariantConstructor::Brace => quote! { {} },
}
}
}
@ -104,9 +109,15 @@ pub(crate) fn parse_enum(data: &DataEnum) -> MacroResult<Enum> {
// Convert the values in the Dict to the appropriate types
let name_opt = dict.remove_typed(NAME, Value::value_string)?;
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(CASE_INSENSITIVE, (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(
CASE_INSENSITIVE,
(false, variant_span),
Value::value_bool,
)?;
// Return an error if there are any unrecognised keys in the Dict
dict.assert_empty()?;
@ -121,10 +132,7 @@ pub(crate) fn parse_enum(data: &DataEnum) -> MacroResult<Enum> {
// Return an error if there is already an "other" variant for this enum
if other_variant {
return Err(MacroError::new(
format!(
"cannot have multiple variants marked as {}",
OTHER
),
format!("cannot have multiple variants marked as {}", OTHER),
other_span,
));
}
@ -136,7 +144,9 @@ pub(crate) fn parse_enum(data: &DataEnum) -> MacroResult<Enum> {
return Err(MacroError::new(
format!(
"cannot use {} for variant {} because it is marked as {}",
NAME, variant.ident.to_string(), OTHER
NAME,
variant.ident,
OTHER
),
name_span,
));
@ -147,14 +157,18 @@ pub(crate) fn parse_enum(data: &DataEnum) -> MacroResult<Enum> {
return Err(MacroError::new(
format!(
"the variant {} must have exactly one field because it is marked as {}",
variant.ident.to_string(), OTHER
variant.ident,
OTHER
),
variant_span,
));
}
// Get the name of the variant's field (or None if it is unnamed)
let field_name = variant.fields.iter().next()
let field_name = variant
.fields
.iter()
.next()
.and_then(|field| field.ident.as_ref());
Variant {
@ -166,7 +180,7 @@ pub(crate) fn parse_enum(data: &DataEnum) -> MacroResult<Enum> {
// Use the str name if one is provided, otherwise use the variant's name
let (name, name_span) = match name_opt {
Some((name, name_span)) => (name, name_span),
None => (variant.ident.to_string(), variant.ident.span())
None => (variant.ident.to_string(), variant.ident.span()),
};
// Do not allow duplicate names
@ -181,7 +195,9 @@ pub(crate) fn parse_enum(data: &DataEnum) -> MacroResult<Enum> {
// Extra duplicate checking for case-insensitive names
let lowercase_name = name.to_lowercase();
if taken_insensitive_names.contains(&lowercase_name) || (case_insensitive && taken_sensitive_names.contains(&lowercase_name)) {
if taken_insensitive_names.contains(&lowercase_name)
|| (case_insensitive && taken_sensitive_names.contains(&lowercase_name))
{
return Err(MacroError::new(
format!("duplicate name \"{}\"", name),
name_span,
@ -192,17 +208,17 @@ pub(crate) fn parse_enum(data: &DataEnum) -> MacroResult<Enum> {
&mut taken_insensitive_names
} else {
&mut taken_sensitive_names
}.insert(lowercase_name);
}
.insert(lowercase_name);
// Return an error if the variant has any fields
if !variant.fields.is_empty() {
let variant_ident = variant.ident.to_string();
return Err(MacroError::new(
format!(
"the variant {} must not have any fields\n\
hint: if you do not want to remove {}\'s fields, try using \
#[enumscribe(ignore)] for {}",
variant_ident, variant_ident, variant_ident
variant.ident, variant.ident, variant.ident
),
variant_span,
));
@ -218,7 +234,11 @@ pub(crate) fn parse_enum(data: &DataEnum) -> MacroResult<Enum> {
Variant {
data: variant,
v_type: VariantType::Named { name, constructor, case_insensitive },
v_type: VariantType::Named {
name,
constructor,
case_insensitive,
},
span: variant_span,
}
};
@ -227,4 +247,4 @@ pub(crate) fn parse_enum(data: &DataEnum) -> MacroResult<Enum> {
}
Ok(Enum { variants })
}
}

@ -19,7 +19,10 @@ pub(crate) struct MacroError {
pub(crate) type MacroResult<T> = result::Result<T, MacroError>;
impl MacroError {
pub(crate) fn new<T>(message: T, span: Span) -> Self where T: Into<Cow<'static, str>> {
pub(crate) fn new<T>(message: T, span: Span) -> Self
where
T: Into<Cow<'static, str>>,
{
MacroError {
message: message.into(),
span,

@ -14,10 +14,10 @@ use syn::{Data, DataEnum, DeriveInput};
use error::{MacroError, MacroResult};
use crate::enums::{Variant, VariantType, Enum};
use crate::enums::{Enum, Variant, VariantType};
mod enums;
mod attribute;
mod enums;
mod error;
const CRATE_ATTR: &'static str = "enumscribe";
@ -33,7 +33,7 @@ macro_rules! proc_try {
($x:expr) => {
match $x {
Ok(val) => val,
Err(err) => return err.into()
Err(err) => return err.into(),
}
};
}
@ -46,13 +46,12 @@ fn gen_scribe_impl<F, G, E>(
other_fn: G,
ignore_err_fn: E,
) -> TokenStream
where
F: Fn(&Variant, &Ident, &str) -> MacroResult<TokenStream2>,
G: Fn(&Variant, &Ident, TokenStream2) -> MacroResult<TokenStream2>,
E: Fn(&Variant, &Ident) -> MacroError
where
F: Fn(&Variant, &Ident, &str) -> MacroResult<TokenStream2>,
G: Fn(&Variant, &Ident, TokenStream2) -> MacroResult<TokenStream2>,
E: Fn(&Variant, &Ident) -> MacroError,
{
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 parsed_enum = proc_try!(enums::parse_enum(enum_data));
@ -65,7 +64,7 @@ fn gen_scribe_impl<F, G, E>(
match variant.match_variant(enum_ident, &named_fn, &other_fn) {
Ok(Some((pattern, result))) => match_arms.push(quote! { #pattern => #result }),
Ok(None) => return ignore_err_fn(variant, enum_ident).into(),
Err(err) => return err.into()
Err(err) => return err.into(),
}
}
@ -77,7 +76,8 @@ fn gen_scribe_impl<F, G, E>(
}
}
}
}).into()
})
.into()
}
fn gen_try_scribe_impl<F, G>(
@ -88,12 +88,11 @@ fn gen_try_scribe_impl<F, G>(
other_fn: G,
ignore_result: TokenStream2,
) -> TokenStream
where
F: Fn(&Variant, &Ident, &str) -> MacroResult<TokenStream2>,
G: Fn(&Variant, &Ident, TokenStream2) -> MacroResult<TokenStream2>
where
F: Fn(&Variant, &Ident, &str) -> MacroResult<TokenStream2>,
G: Fn(&Variant, &Ident, TokenStream2) -> MacroResult<TokenStream2>,
{
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 parsed_enum = proc_try!(enums::parse_enum(enum_data));
@ -107,7 +106,7 @@ fn gen_try_scribe_impl<F, G>(
match variant.match_variant(enum_ident, &named_fn, &other_fn) {
Ok(Some((pattern, result))) => match_arms.push(quote! { #pattern => #result }),
Ok(None) => ignore_variant = true,
Err(err) => return err.into()
Err(err) => return err.into(),
}
}
@ -126,7 +125,8 @@ fn gen_try_scribe_impl<F, G>(
}
}
}
}).into()
})
.into()
}
fn gen_unscribe_impl<F, G, E>(
@ -138,13 +138,12 @@ fn gen_unscribe_impl<F, G, E>(
other_fn: G,
other_missing_fn: E,
) -> TokenStream
where
F: Fn(TokenStream2) -> TokenStream2,
G: Fn(TokenStream2) -> TokenStream2,
E: Fn(&Ident) -> MacroResult<TokenStream2>
where
F: Fn(TokenStream2) -> TokenStream2,
G: Fn(TokenStream2) -> TokenStream2,
E: Fn(&Ident) -> MacroResult<TokenStream2>,
{
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 parsed_enum = proc_try!(enums::parse_enum(enum_data));
@ -154,7 +153,12 @@ fn gen_unscribe_impl<F, G, E>(
let to_unscribe_ident = quote! { __enumscribe_to_unscribe };
let main_match = proc_try!(gen_unscribe_match(
enum_ident, &parsed_enum, &to_unscribe_ident, named_fn, other_fn, other_missing_fn
enum_ident,
&parsed_enum,
&to_unscribe_ident,
named_fn,
other_fn,
other_missing_fn
));
(quote! {
@ -163,7 +167,8 @@ fn gen_unscribe_impl<F, G, E>(
#main_match
}
}
}).into()
})
.into()
}
fn gen_unscribe_match<F, G, E>(
@ -174,10 +179,10 @@ fn gen_unscribe_match<F, G, E>(
other_fn: G,
other_missing_fn: E,
) -> MacroResult<TokenStream2>
where
F: Fn(TokenStream2) -> TokenStream2,
G: Fn(TokenStream2) -> TokenStream2,
E: Fn(&Ident) -> MacroResult<TokenStream2>
where
F: Fn(TokenStream2) -> TokenStream2,
G: Fn(TokenStream2) -> TokenStream2,
E: Fn(&Ident) -> MacroResult<TokenStream2>,
{
let mut other_arm = None;
let mut case_sensitive_arms = Vec::new();
@ -189,7 +194,11 @@ fn gen_unscribe_match<F, G, E>(
match &variant.v_type {
VariantType::Ignore => (),
VariantType::Named { name, constructor, case_insensitive } => {
VariantType::Named {
name,
constructor,
case_insensitive,
} => {
let match_pattern = if *case_insensitive {
let lowercase_name = name.to_lowercase();
quote! { #lowercase_name }
@ -198,18 +207,21 @@ fn gen_unscribe_match<F, G, E>(
};
let constructor_tokens = constructor.empty();
let constructed_variant = quote! { #enum_ident::#variant_ident #constructor_tokens };
let constructed_variant =
quote! { #enum_ident::#variant_ident #constructor_tokens };
let match_result = named_fn(constructed_variant);
if *case_insensitive {
&mut case_insensitive_arms
} else {
&mut case_sensitive_arms
}.push(quote! { #match_pattern => #match_result });
}
.push(quote! { #match_pattern => #match_result });
}
VariantType::Other { field_name } => {
let unscribe_value = quote! { <_ as ::std::convert::Into<_>>::into(#match_against) };
let unscribe_value =
quote! { <_ as ::std::convert::Into<_>>::into(#match_against) };
let constructed_variant = match field_name {
None => quote! {
@ -217,7 +229,7 @@ fn gen_unscribe_match<F, G, E>(
},
Some(field_name) => quote! {
#enum_ident::#variant_ident { #field_name: #unscribe_value }
}
},
};
let match_result = other_fn(constructed_variant);
@ -229,7 +241,7 @@ fn gen_unscribe_match<F, G, E>(
let other_arm = match other_arm {
Some(other_arm) => other_arm,
None => other_missing_fn(enum_ident)?
None => other_missing_fn(enum_ident)?,
};
let case_insensitive_match = if case_insensitive_arms.is_empty() {
@ -258,16 +270,14 @@ fn gen_unscribe_match<F, G, E>(
}
},
(true, Some(case_insensitive_match)) => {
case_insensitive_match
}
(true, Some(case_insensitive_match)) => case_insensitive_match,
(false, Some(case_insensitive_match)) => quote! {
match #match_against {
#(#case_sensitive_arms,)*
_ => { #case_insensitive_match },
}
}
},
};
Ok(main_match)
@ -301,26 +311,32 @@ fn gen_unscribe_match<F, G, E>(
pub fn derive_scribe_static_str(input: TokenStream) -> TokenStream {
gen_scribe_impl(
input,
quote! { ::enumscribe::ScribeStaticStr },
quote! { &'static str },
|_, _, name| Ok(quote! { #name }),
|variant, enum_ident, _| Err(MacroError::new(format!(
"cannot derive ScribeStaticStr for {} because the variant {} is marked as {}, so \
there is no &'static str associated with it\n\
hint: try deriving ScribeCowStr instead",
enum_ident, variant.data.ident, OTHER
), variant.span)),
|variant, enum_ident| MacroError::new(format!(
"cannot derive ScribeStaticStr for {} because the variant {} is marked as {}\n\
explanation: since {} is ignored, it cannot be guaranteed that the enum can \
always be successfully converted to a String\n\
hint: try deriving TryScribeStaticStr instead",
enum_ident, variant.data.ident, IGNORE, variant.data.ident
), variant.span),
|variant, enum_ident, _| {
Err(MacroError::new(
format!(
"cannot derive ScribeStaticStr for {} because the variant {} is marked as {}, so \
there is no &'static str associated with it\n\
hint: try deriving ScribeCowStr instead",
enum_ident, variant.data.ident, OTHER
),
variant.span,
))
},
|variant, enum_ident| {
MacroError::new(
format!(
"cannot derive ScribeStaticStr for {} because the variant {} is marked as {}\n\
explanation: since {} is ignored, it cannot be guaranteed that the enum can \
always be successfully converted to a String\n\
hint: try deriving TryScribeStaticStr instead",
enum_ident, variant.data.ident, IGNORE, variant.data.ident
),
variant.span,
)
},
)
}
@ -343,21 +359,24 @@ pub fn derive_scribe_static_str(input: TokenStream) -> TokenStream {
pub fn derive_try_scribe_static_str(input: TokenStream) -> TokenStream {
gen_try_scribe_impl(
input,
quote! { ::enumscribe::TryScribeStaticStr },
quote! { ::std::option::Option<&'static str> },
|_, _, name| Ok(quote! {
::std::option::Option::Some(#name)
}),
|variant, enum_ident, _| Err(MacroError::new(format!(
"cannot derive TryScribeStaticStr for {} because the variant {} is marked as {}, so \
there is no &'static str associated with it\n\
hint: try deriving TryScribeCowStr instead",
enum_ident, variant.data.ident, OTHER
), variant.span)),
|_, _, name| {
Ok(quote! {
::std::option::Option::Some(#name)
})
},
|variant, enum_ident, _| {
Err(MacroError::new(
format!(
"cannot derive TryScribeStaticStr for {} because the variant {} is marked as {}, so \
there is no &'static str associated with it\n\
hint: try deriving TryScribeCowStr instead",
enum_ident, variant.data.ident, OTHER
),
variant.span,
))
},
quote! { ::std::option::Option::None },
)
}
@ -375,25 +394,30 @@ pub fn derive_try_scribe_static_str(input: TokenStream) -> TokenStream {
pub fn derive_scribe_string(input: TokenStream) -> TokenStream {
gen_scribe_impl(
input,
quote! { ::enumscribe::ScribeString },
quote! { ::std::string::String },
|_, _, name| Ok(quote! {
<_ as ::std::borrow::ToOwned>::to_owned(#name)
}),
|_, _, field| Ok(quote! {
<_ as ::std::convert::Into<::std::string::String>>::into(#field)
}),
|variant, enum_ident| MacroError::new(format!(
"cannot derive ScribeString for {} because the variant {} is marked as {}\n\
explanation: since {} is ignored, it cannot be guaranteed that the enum can \
always be successfully converted to a String\n\
hint: try deriving TryScribeString instead",
enum_ident, variant.data.ident, IGNORE, variant.data.ident
), variant.span),
|_, _, name| {
Ok(quote! {
<_ as ::std::borrow::ToOwned>::to_owned(#name)
})
},
|_, _, field| {
Ok(quote! {
<_ as ::std::convert::Into<::std::string::String>>::into(#field)
})
},
|variant, enum_ident| {
MacroError::new(
format!(
"cannot derive ScribeString for {} because the variant {} is marked as {}\n\
explanation: since {} is ignored, it cannot be guaranteed that the enum can \
always be successfully converted to a String\n\
hint: try deriving TryScribeString instead",
enum_ident, variant.data.ident, IGNORE, variant.data.ident
),
variant.span,
)
},
)
}
@ -410,22 +434,22 @@ pub fn derive_scribe_string(input: TokenStream) -> TokenStream {
pub fn derive_try_scribe_string(input: TokenStream) -> TokenStream {
gen_try_scribe_impl(
input,
quote! { ::enumscribe::TryScribeString },
quote! { ::std::option::Option<::std::string::String> },
|_, _, name| Ok(quote! {
::std::option::Option::Some(
<_ as ::std::borrow::ToOwned>::to_owned(#name)
)
}),
|_, _, field| Ok(quote! {
::std::option::Option::Some(
<_ as ::std::convert::Into<::std::string::String>>::into(#field)
)
}),
|_, _, name| {
Ok(quote! {
::std::option::Option::Some(
<_ as ::std::borrow::ToOwned>::to_owned(#name)
)
})
},
|_, _, field| {
Ok(quote! {
::std::option::Option::Some(
<_ as ::std::convert::Into<::std::string::String>>::into(#field)
)
})
},
quote! { ::std::option::Option::None },
)
}
@ -453,27 +477,32 @@ pub fn derive_try_scribe_string(input: TokenStream) -> TokenStream {
pub fn derive_scribe_cow_str(input: TokenStream) -> TokenStream {
gen_scribe_impl(
input,
quote! { ::enumscribe::ScribeCowStr },
quote! { ::std::borrow::Cow<'static, str> },
|_, _, name| Ok(quote! {
::std::borrow::Cow::Borrowed(#name)
}),
|_, _, field| Ok(quote! {
::std::borrow::Cow::Owned(
<_ as ::std::convert::Into<::std::string::String>>::into(#field)
|_, _, name| {
Ok(quote! {
::std::borrow::Cow::Borrowed(#name)
})
},
|_, _, field| {
Ok(quote! {
::std::borrow::Cow::Owned(
<_ as ::std::convert::Into<::std::string::String>>::into(#field)
)
})
},
|variant, enum_ident| {
MacroError::new(
format!(
"cannot derive ScribeCowStr for {} because the variant {} is marked as {}\n\
explanation: since {} is ignored, it cannot be guaranteed that the enum can \
always be successfully converted to a String\n\
hint: try deriving TryScribeCowStr instead",
enum_ident, variant.data.ident, IGNORE, variant.data.ident
),
variant.span,
)
}),
|variant, enum_ident| MacroError::new(format!(
"cannot derive ScribeCowStr for {} because the variant {} is marked as {}\n\
explanation: since {} is ignored, it cannot be guaranteed that the enum can \
always be successfully converted to a String\n\
hint: try deriving TryScribeCowStr instead",
enum_ident, variant.data.ident, IGNORE, variant.data.ident
), variant.span),
},
)
}
@ -501,24 +530,24 @@ pub fn derive_scribe_cow_str(input: TokenStream) -> TokenStream {
pub fn derive_try_scribe_cow_str(input: TokenStream) -> TokenStream {
gen_try_scribe_impl(
input,
quote! { ::enumscribe::TryScribeCowStr },
quote! { ::std::option::Option<::std::borrow::Cow<'static, str>> },
|_, _, name| Ok(quote! {
::std::option::Option::Some(
::std::borrow::Cow::Borrowed(#name)
)
}),
|_, _, field| Ok(quote! {
::std::option::Option::Some(
::std::borrow::Cow::Owned(
<_ as ::std::convert::Into<::std::string::String>>::into(#field)
|_, _, name| {
Ok(quote! {
::std::option::Option::Some(
::std::borrow::Cow::Borrowed(#name)
)
)
}),
})
},
|_, _, field| {
Ok(quote! {
::std::option::Option::Some(
::std::borrow::Cow::Owned(
<_ as ::std::convert::Into<::std::string::String>>::into(#field)
)
)
})
},
quote! { ::std::option::Option::None },
)
}
@ -548,22 +577,23 @@ pub fn derive_try_scribe_cow_str(input: TokenStream) -> TokenStream {
pub fn derive_unscribe(input: TokenStream) -> TokenStream {
gen_unscribe_impl(
input,
quote! { ::enumscribe::Unscribe },
quote! { unscribe },
quote! { Self },
|constructed_named_variant| constructed_named_variant,
|constructed_other_variant| constructed_other_variant,
|enum_ident| Err(MacroError::new(format!(
"cannot derive Unscribe for {} because no variant is marked as {}\n\
explanation: since there is no {} variant, it cannot be guaranteed that every string \
can be successfully converted to a variant of {}\n\
hint: either introduce an {} variant, or try deriving TryUnscribe instead",
enum_ident, OTHER, OTHER, enum_ident, OTHER
), enum_ident.span())),
|enum_ident| {
Err(MacroError::new(
format!(
"cannot derive Unscribe for {} because no variant is marked as {}\n\
explanation: since there is no {} variant, it cannot be guaranteed that every string \
can be successfully converted to a variant of {}\n\
hint: either introduce an {} variant, or try deriving TryUnscribe instead",
enum_ident, OTHER, OTHER, enum_ident, OTHER
),
enum_ident.span(),
))
},
)
}
@ -589,15 +619,11 @@ pub fn derive_unscribe(input: TokenStream) -> TokenStream {
pub fn derive_try_unscribe(input: TokenStream) -> TokenStream {
gen_unscribe_impl(
input,
quote! { ::enumscribe::TryUnscribe },
quote! { try_unscribe },
quote! { ::std::option::Option<Self> },
|constructed_named_variant| quote! { ::std::option::Option::Some(#constructed_named_variant) },
|constructed_other_variant| quote! { ::std::option::Option::Some(#constructed_other_variant) },
|_| Ok(quote! { _ => ::std::option::Option::None }),
)
}
@ -614,8 +640,7 @@ pub fn derive_try_unscribe(input: TokenStream) -> TokenStream {
#[cfg(feature = "serde")]
#[proc_macro_derive(EnumSerialize, attributes(enumscribe))]
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 parsed_enum = proc_try!(enums::parse_enum(enum_data));
@ -632,7 +657,9 @@ pub fn derive_enum_serialize(input: TokenStream) -> TokenStream {
match &variant.v_type {
VariantType::Ignore => ignore_variant = true,
VariantType::Named { name, constructor, .. } => {
VariantType::Named {
name, constructor, ..
} => {
let constructor_tokens = constructor.empty();
match_arms.push(quote! {
#enum_ident::#variant_ident #constructor_tokens =>
@ -640,23 +667,19 @@ pub fn derive_enum_serialize(input: TokenStream) -> TokenStream {
})
}
VariantType::Other { field_name } => {
match field_name {
Some(field_name) => {
match_arms.push(quote! {
#enum_ident::#variant_ident { #field_name } =>
#serializer_ident.serialize_str(&#field_name)
})
}
None => {
let field_name = quote! { __enumscribe_other_inner };
match_arms.push(quote! {
#enum_ident::#variant_ident(#field_name) =>
#serializer_ident.serialize_str(&#field_name)
})
}
VariantType::Other { field_name } => match field_name {
Some(field_name) => match_arms.push(quote! {
#enum_ident::#variant_ident { #field_name } =>
#serializer_ident.serialize_str(&#field_name)
}),
None => {
let field_name = quote! { __enumscribe_other_inner };
match_arms.push(quote! {
#enum_ident::#variant_ident(#field_name) =>
#serializer_ident.serialize_str(&#field_name)
})
}
}
},
}
}
@ -685,7 +708,8 @@ pub fn derive_enum_serialize(input: TokenStream) -> TokenStream {
}
}
}
}).into()
})
.into()
}
/// Derives `serde::Deserialize` for an enum.
@ -707,8 +731,7 @@ pub fn derive_enum_serialize(input: TokenStream) -> TokenStream {
#[cfg(feature = "serde")]
#[proc_macro_derive(EnumDeserialize, attributes(enumscribe))]
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 parsed_enum = proc_try!(enums::parse_enum(enum_data));
@ -719,10 +742,12 @@ pub fn derive_enum_deserialize(input: TokenStream) -> TokenStream {
let deserialized_string_ident = quote! { __enumscribe_deserialized_string };
let deserialized_str_ident = quote! { __enumscribe_deserialized_str };
let variant_strings = parsed_enum.variants.iter()
let variant_strings = parsed_enum
.variants
.iter()
.map(|variant| match &variant.v_type {
VariantType::Named { name, .. } => Some(name.as_str()),
_ => None
_ => None,
})
.filter(|name| name.is_some())
.map(|name| name.unwrap())
@ -759,18 +784,32 @@ pub fn derive_enum_deserialize(input: TokenStream) -> TokenStream {
#main_match
}
}
}).into()
})
.into()
}
fn get_enum_data(input: &DeriveInput) -> MacroResult<&DataEnum> {
let enum_data = match &input.data {
Data::Enum(enum_data) => enum_data,
Data::Struct(_) => return Err(MacroError::new("enumscribe cannot be used for structs", input.ident.span())),
Data::Union(_) => return Err(MacroError::new("enumscribe cannot be used for unions", input.ident.span()))
Data::Struct(_) => {
return Err(MacroError::new(
"enumscribe cannot be used for structs",
input.ident.span(),
))
}
Data::Union(_) => {
return Err(MacroError::new(
"enumscribe cannot be used for unions",
input.ident.span(),
))
}
};
if enum_data.variants.is_empty() {
return Err(MacroError::new("enumscribe cannot be used for empty enums", input.ident.span()));
return Err(MacroError::new(
"enumscribe cannot be used for empty enums",
input.ident.span(),
));
}
Ok(enum_data)

@ -39,12 +39,16 @@ fn main() {
let info = AirportInfo {
airport: Airport::Gatwick(),
info: "It's somewhere in London, innit".to_owned()
info: "It's somewhere in London, innit".to_owned(),
};
println!("{}", serde_json::to_string(&info).unwrap());
println!();
let info_str = r#"{ "airport": "lgw", "info": "Fun Gatwick fact: I'm fresh out of Gatwick facts" }"#;
println!("{:?}", serde_json::from_str::<AirportInfo>(info_str).unwrap());
let info_str =
r#"{ "airport": "lgw", "info": "Fun Gatwick fact: I'm fresh out of Gatwick facts" }"#;
println!(
"{:?}",
serde_json::from_str::<AirportInfo>(info_str).unwrap()
);
}

@ -45,7 +45,7 @@ fn case_insensitivity_example() {
fn other_example() {
use std::borrow::Cow;
use enumscribe::{Unscribe, ScribeCowStr};
use enumscribe::{ScribeCowStr, Unscribe};
#[derive(ScribeCowStr, Unscribe, PartialEq, Eq, Debug)]
enum Website {
@ -61,12 +61,21 @@ fn other_example() {
assert_eq!(Website::unscribe("github.com"), Website::Github);
// Unbelievably, there exist websites other than github and crates.io
assert_eq!(Website::unscribe("stackoverflow.com"), Website::Other("stackoverflow.com".to_owned()));
assert_eq!(
Website::unscribe("stackoverflow.com"),
Website::Other("stackoverflow.com".to_owned())
);
// We can't scribe to a &'static str anymore, so we use a Cow<'static, str> instead
assert_eq!(Website::Github.scribe(), Cow::Borrowed::<'static, str>("github.com"));
assert_eq!(Website::Other("owasp.org".to_owned()).scribe(), Cow::Owned::<'static, str>("owasp.org".to_owned()));
assert_eq!(
Website::Github.scribe(),
Cow::Borrowed::<'static, str>("github.com")
);
assert_eq!(
Website::Other("owasp.org".to_owned()).scribe(),
Cow::Owned::<'static, str>("owasp.org".to_owned())
);
}
fn ignore_example() {
@ -90,9 +99,9 @@ fn ignore_example() {
}
fn serde_example() {
use serde::{Serialize, Deserialize};
use serde::{Deserialize, Serialize};
use enumscribe::{EnumSerialize, EnumDeserialize};
use enumscribe::{EnumDeserialize, EnumSerialize};
#[derive(EnumSerialize, EnumDeserialize, PartialEq, Eq, Clone, Copy, Debug)]
enum Airport {
@ -118,7 +127,10 @@ fn serde_example() {
let flight_json = r#"{"takeoff":"LHR","landing":"LGW"}"#;
assert_eq!(serde_json::to_string(&flight).unwrap(), flight_json.to_owned());
assert_eq!(
serde_json::to_string(&flight).unwrap(),
flight_json.to_owned()
);
assert_eq!(serde_json::from_str::<Flight>(flight_json).unwrap(), flight);
}

@ -1,17 +1,12 @@
use std::borrow::Cow;
use enumscribe::{
ScribeCowStr,
ScribeStaticStr,
ScribeString,
TryScribeCowStr,
TryScribeStaticStr,
ScribeCowStr, ScribeStaticStr, ScribeString, TryScribeCowStr, TryScribeStaticStr,
TryScribeString,
};
const TEST_STRINGS: [&'static str; 6] = [
"", "\0", "foo", "baa", "Hello, world!", "こんにちは、世界"
];
const TEST_STRINGS: [&'static str; 6] =
["", "\0", "foo", "baa", "Hello, world!", "こんにちは、世界"];
#[test]
fn test_scribe_static_str() {
@ -54,7 +49,10 @@ fn test_try_scribe_static_str() {
#[enumscribe(ignore)]
V7(i32, i32),
#[enumscribe(ignore)]
V8 { s: &'static str, x: i32 },
V8 {
s: &'static str,
x: i32,
},
}
assert_eq!(E0::V0.try_scribe(), Some("V0"));
@ -65,7 +63,14 @@ fn test_try_scribe_static_str() {
assert_eq!(E0::V5 {}.try_scribe(), Some("baz"));
assert_eq!(E0::V6.try_scribe(), None);
assert_eq!(E0::V7(123, 456).try_scribe(), None);
assert_eq!(E0::V8 { s: "lorem ipsum", x: 246 }.try_scribe(), None);
assert_eq!(
E0::V8 {
s: "lorem ipsum",
x: 246
}
.try_scribe(),
None
);
}
#[test]
@ -135,7 +140,10 @@ fn test_try_scribe_string() {
#[enumscribe(ignore)]
V7(i32, i32),
#[enumscribe(ignore)]
V8 { s: &'static str, x: i32 },
V8 {
s: &'static str,
x: i32,
},
}
assert_eq!(E0::V0.try_scribe(), Some("V0".to_owned()));
@ -146,7 +154,14 @@ fn test_try_scribe_string() {
assert_eq!(E0::V5 {}.try_scribe(), Some("baz".to_owned()));
assert_eq!(E0::V6.try_scribe(), None);
assert_eq!(E0::V7(123, 456).try_scribe(), None);
assert_eq!(E0::V8 { s: "lorem ipsum", x: 246 }.try_scribe(), None);
assert_eq!(
E0::V8 {
s: "lorem ipsum",
x: 246
}
.try_scribe(),
None
);
#[derive(TryScribeString, Eq, PartialEq, Debug)]
enum E1 {
@ -213,7 +228,10 @@ fn test_scribe_cow_str() {
assert_eq!(E1::V0.scribe(), Cow::Borrowed("foo"));
for &x in &TEST_STRINGS {
assert_eq!(E1::V1(x.to_owned()).scribe(), Cow::Owned::<'static, str>(x.to_owned()));
assert_eq!(
E1::V1(x.to_owned()).scribe(),
Cow::Owned::<'static, str>(x.to_owned())
);
}
#[derive(ScribeCowStr, Eq, PartialEq, Debug)]
@ -226,7 +244,10 @@ fn test_scribe_cow_str() {
assert_eq!(E2::V0.scribe(), "foo".to_owned());
for &x in &TEST_STRINGS {
assert_eq!(E2::V1 { s: x.to_owned() }.scribe(), Cow::Owned::<'static, str>(x.to_owned()));
assert_eq!(
E2::V1 { s: x.to_owned() }.scribe(),
Cow::Owned::<'static, str>(x.to_owned())
);
}
}
@ -248,7 +269,10 @@ fn test_try_scribe_cow_str() {
#[enumscribe(ignore)]
V7(i32, i32),
#[enumscribe(ignore)]
V8 { s: &'static str, x: i32 },
V8 {
s: &'static str,
x: i32,
},
}
assert_eq!(E0::V0.try_scribe(), Some(Cow::Borrowed("V0")));
@ -259,7 +283,14 @@ fn test_try_scribe_cow_str() {
assert_eq!(E0::V5 {}.try_scribe(), Some(Cow::Borrowed("baz")));
assert_eq!(E0::V6.try_scribe(), None);
assert_eq!(E0::V7(123, 456).try_scribe(), None);
assert_eq!(E0::V8 { s: "lorem ipsum", x: 246 }.try_scribe(), None);
assert_eq!(
E0::V8 {
s: "lorem ipsum",
x: 246
}
.try_scribe(),
None
);
#[derive(TryScribeCowStr, Eq, PartialEq, Debug)]
enum E1 {
@ -273,7 +304,10 @@ fn test_try_scribe_cow_str() {
assert_eq!(E1::V0.try_scribe(), Some(Cow::Borrowed("foo")));
for &x in &TEST_STRINGS {
assert_eq!(E1::V1(x.to_owned()).try_scribe(), Some(Cow::Owned::<'static, str>(x.to_owned())));
assert_eq!(
E1::V1(x.to_owned()).try_scribe(),
Some(Cow::Owned::<'static, str>(x.to_owned()))
);
}
assert_eq!(E1::V2.try_scribe(), None);
@ -289,7 +323,10 @@ fn test_try_scribe_cow_str() {
assert_eq!(E2::V0.try_scribe(), Some(Cow::Borrowed("foo")));
for &x in &TEST_STRINGS {
assert_eq!(E2::V1 { s: x.to_owned() }.try_scribe(), Some(Cow::Owned::<'static, str>(x.to_owned())));
assert_eq!(
E2::V1 { s: x.to_owned() }.try_scribe(),
Some(Cow::Owned::<'static, str>(x.to_owned()))
);
}
assert_eq!(E2::V2.try_scribe(), None);
}

@ -279,24 +279,42 @@ fn test_try_unscribe() {
assert_eq!(E1::try_unscribe("loREM"), Some(E1::V7));
assert_eq!(E1::try_unscribe("larem"), Some(E1::V12("larem".to_owned())));
assert_eq!(E1::try_unscribe("lore"), Some(E1::V12("lore".to_owned())));
assert_eq!(E1::try_unscribe("llorem"), Some(E1::V12("llorem".to_owned())));
assert_eq!(E1::try_unscribe("loremm"), Some(E1::V12("loremm".to_owned())));
assert_eq!(
E1::try_unscribe("llorem"),
Some(E1::V12("llorem".to_owned()))
);
assert_eq!(
E1::try_unscribe("loremm"),
Some(E1::V12("loremm".to_owned()))
);
assert_eq!(E1::try_unscribe("ipsum"), Some(E1::V9()));
assert_eq!(E1::try_unscribe("IPSUM"), Some(E1::V9()));
assert_eq!(E1::try_unscribe("IpSuM"), Some(E1::V9()));
assert_eq!(E1::try_unscribe("ipSUM"), Some(E1::V9()));
assert_eq!(E1::try_unscribe("ipdum"), Some(E1::V12("ipdum".to_owned())));
assert_eq!(E1::try_unscribe("ipsu"), Some(E1::V12("ipsu".to_owned())));
assert_eq!(E1::try_unscribe("iipsum"), Some(E1::V12("iipsum".to_owned())));
assert_eq!(E1::try_unscribe("ipsumm"), Some(E1::V12("ipsumm".to_owned())));
assert_eq!(
E1::try_unscribe("iipsum"),
Some(E1::V12("iipsum".to_owned()))
);
assert_eq!(
E1::try_unscribe("ipsumm"),
Some(E1::V12("ipsumm".to_owned()))
);
assert_eq!(E1::try_unscribe("dolor"), Some(E1::V11 {}));
assert_eq!(E1::try_unscribe("DOLOR"), Some(E1::V11 {}));
assert_eq!(E1::try_unscribe("DoLoR"), Some(E1::V11 {}));
assert_eq!(E1::try_unscribe("doLOR"), Some(E1::V11 {}));
assert_eq!(E1::try_unscribe("doler"), Some(E1::V12("doler".to_owned())));
assert_eq!(E1::try_unscribe("dolo"), Some(E1::V12("dolo".to_owned())));
assert_eq!(E1::try_unscribe("ddolor"), Some(E1::V12("ddolor".to_owned())));
assert_eq!(E1::try_unscribe("dolorr"), Some(E1::V12("dolorr".to_owned())));
assert_eq!(
E1::try_unscribe("ddolor"),
Some(E1::V12("ddolor".to_owned()))
);
assert_eq!(
E1::try_unscribe("dolorr"),
Some(E1::V12("dolorr".to_owned()))
);
assert_eq!(E1::try_unscribe(""), Some(E1::V12("".to_owned())));
assert_eq!(E1::try_unscribe("\0"), Some(E1::V12("\0".to_owned())));
}

Loading…
Cancel
Save