diff --git a/enumscribe_derive/src/attribute.rs b/enumscribe_derive/src/attribute.rs index cbbb086..676e684 100644 --- a/enumscribe_derive/src/attribute.rs +++ b/enumscribe_derive/src/attribute.rs @@ -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 { 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::()); @@ -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(&mut self, key: &str, converter: F) -> MacroResult> - where - F: Fn(&Value) -> ValueTypeResult + pub(crate) fn remove_typed( + &mut self, + key: &str, + converter: F, + ) -> MacroResult> + where + F: Fn(&Value) -> ValueTypeResult, { 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(&mut self, key: &str, default: (T, Span), converter: F) -> MacroResult<(T, Span)> - where - F: Fn(&Value) -> ValueTypeResult + pub(crate) fn remove_typed_or_default( + &mut self, + key: &str, + default: (T, Span), + converter: F, + ) -> MacroResult<(T, Span)> + where + F: Fn(&Value) -> ValueTypeResult, { match self.remove_typed(key, converter) { Ok(Some(value)) => Ok(value), @@ -171,15 +184,14 @@ impl Parse for AttributeTag { .parse_terminated::(KeyValPair::parse)? .into_iter() .map(|pair| (pair.key, pair.val, pair.span)) - .collect::>() + .collect::>(), }) } } impl Parse for KeyValPair { fn parse(input: ParseStream) -> syn::Result { - let key = input - .parse::()?; + let key = input.parse::()?; let val = if input.peek(Token![=]) { input.parse::()?; @@ -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(input: ParseStream) -> syn::Result where T: Parse { +fn speculative_parse(input: ParseStream) -> syn::Result +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(input: ParseStream) -> syn::Result<(ParseBuffer, T)> where T: Parse { +fn fork_and_parse(input: ParseStream) -> syn::Result<(ParseBuffer, T)> +where + T: Parse, +{ let fork = input.fork(); T::parse(&fork).map(move |parsed| (fork, parsed)) } diff --git a/enumscribe_derive/src/enums.rs b/enumscribe_derive/src/enums.rs index cafee04..1a24e15 100644 --- a/enumscribe_derive/src/enums.rs +++ b/enumscribe_derive/src/enums.rs @@ -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> - where - F: Fn(&Variant, &Ident, &str) -> MacroResult, - G: Fn(&Variant, &Ident, TokenStream2) -> MacroResult + where + F: Fn(&Variant, &Ident, &str) -> MacroResult, + G: Fn(&Variant, &Ident, TokenStream2) -> MacroResult, { 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 { // 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 { // 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 { 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 { 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 { // 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 { // 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 { &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 { 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 { } Ok(Enum { variants }) -} \ No newline at end of file +} diff --git a/enumscribe_derive/src/error.rs b/enumscribe_derive/src/error.rs index b6b7941..a27d3c2 100644 --- a/enumscribe_derive/src/error.rs +++ b/enumscribe_derive/src/error.rs @@ -19,7 +19,10 @@ pub(crate) struct MacroError { pub(crate) type MacroResult = result::Result; impl MacroError { - pub(crate) fn new(message: T, span: Span) -> Self where T: Into> { + pub(crate) fn new(message: T, span: Span) -> Self + where + T: Into>, + { MacroError { message: message.into(), span, diff --git a/enumscribe_derive/src/lib.rs b/enumscribe_derive/src/lib.rs index 2a253f4..b747945 100644 --- a/enumscribe_derive/src/lib.rs +++ b/enumscribe_derive/src/lib.rs @@ -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( other_fn: G, ignore_err_fn: E, ) -> TokenStream - where - F: Fn(&Variant, &Ident, &str) -> MacroResult, - G: Fn(&Variant, &Ident, TokenStream2) -> MacroResult, - E: Fn(&Variant, &Ident) -> MacroError +where + F: Fn(&Variant, &Ident, &str) -> MacroResult, + G: Fn(&Variant, &Ident, TokenStream2) -> MacroResult, + 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( 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( } } } - }).into() + }) + .into() } fn gen_try_scribe_impl( @@ -88,12 +88,11 @@ fn gen_try_scribe_impl( other_fn: G, ignore_result: TokenStream2, ) -> TokenStream - where - F: Fn(&Variant, &Ident, &str) -> MacroResult, - G: Fn(&Variant, &Ident, TokenStream2) -> MacroResult +where + F: Fn(&Variant, &Ident, &str) -> MacroResult, + G: Fn(&Variant, &Ident, TokenStream2) -> MacroResult, { - 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( 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( } } } - }).into() + }) + .into() } fn gen_unscribe_impl( @@ -138,13 +138,12 @@ fn gen_unscribe_impl( other_fn: G, other_missing_fn: E, ) -> TokenStream - where - F: Fn(TokenStream2) -> TokenStream2, - G: Fn(TokenStream2) -> TokenStream2, - E: Fn(&Ident) -> MacroResult +where + F: Fn(TokenStream2) -> TokenStream2, + G: Fn(TokenStream2) -> TokenStream2, + E: Fn(&Ident) -> MacroResult, { - 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( 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( #main_match } } - }).into() + }) + .into() } fn gen_unscribe_match( @@ -174,10 +179,10 @@ fn gen_unscribe_match( other_fn: G, other_missing_fn: E, ) -> MacroResult - where - F: Fn(TokenStream2) -> TokenStream2, - G: Fn(TokenStream2) -> TokenStream2, - E: Fn(&Ident) -> MacroResult +where + F: Fn(TokenStream2) -> TokenStream2, + G: Fn(TokenStream2) -> TokenStream2, + E: Fn(&Ident) -> MacroResult, { let mut other_arm = None; let mut case_sensitive_arms = Vec::new(); @@ -189,7 +194,11 @@ fn gen_unscribe_match( 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( }; 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( }, 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( 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( } }, - (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( 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 }, - |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) diff --git a/enumscribe_examples/examples/airports.rs b/enumscribe_examples/examples/airports.rs index 7bdae98..d743b94 100644 --- a/enumscribe_examples/examples/airports.rs +++ b/enumscribe_examples/examples/airports.rs @@ -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::(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::(info_str).unwrap() + ); } diff --git a/enumscribe_examples/examples/readme_examples.rs b/enumscribe_examples/examples/readme_examples.rs index 415c008..7091753 100644 --- a/enumscribe_examples/examples/readme_examples.rs +++ b/enumscribe_examples/examples/readme_examples.rs @@ -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_json).unwrap(), flight); } diff --git a/enumscribe_tests/tests/test_scribe.rs b/enumscribe_tests/tests/test_scribe.rs index c893376..ece610a 100644 --- a/enumscribe_tests/tests/test_scribe.rs +++ b/enumscribe_tests/tests/test_scribe.rs @@ -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); } diff --git a/enumscribe_tests/tests/test_unscribe.rs b/enumscribe_tests/tests/test_unscribe.rs index 1a78793..affef22 100644 --- a/enumscribe_tests/tests/test_unscribe.rs +++ b/enumscribe_tests/tests/test_unscribe.rs @@ -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()))); }