Run cargo fmt, minor code cleanup
This commit is contained in:
1 parent
4317afa003
commit
f9a9db2e9a
8 files changed
+420
-268
No files matched your search
@@ -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,
|
||||
|
||||
+216
-177
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user