diff --git a/enumscribe_derive/src/lib.rs b/enumscribe_derive/src/lib.rs index 1984281..39435c1 100644 --- a/enumscribe_derive/src/lib.rs +++ b/enumscribe_derive/src/lib.rs @@ -27,7 +27,7 @@ macro_rules! proc_try { } #[proc_macro_derive(ScribeString, attributes(enumscribe))] -pub fn derive_enum_to_string(input: TokenStream) -> TokenStream { +pub fn derive_scribe_string(input: TokenStream) -> TokenStream { let input: DeriveInput = syn::parse(input) .expect("failed to parse input"); @@ -42,8 +42,12 @@ pub fn derive_enum_to_string(input: TokenStream) -> TokenStream { for variant in parsed_enum.variants.iter() { match variant.match_variant( - |name| Ok(quote! { <_ as ::std::borrow::ToOwned>::to_owned(#name) }), - |field| Ok(quote! { <_ as ::std::convert::Into<::std::string::String>>::into(#field) }), + |name| Ok(quote! { + <_ as ::std::borrow::ToOwned>::to_owned(#name) + }), + |field| Ok(quote! { + <_ as ::std::convert::Into<::std::string::String>>::into(#field) + }), ) { Ok(Some((pattern, result))) => { match_patterns.push(pattern); @@ -75,7 +79,7 @@ pub fn derive_enum_to_string(input: TokenStream) -> TokenStream { } #[proc_macro_derive(TryScribeString, attributes(enumscribe))] -pub fn derive_try_enum_to_string(input: TokenStream) -> TokenStream { +pub fn derive_try_scribe_string(input: TokenStream) -> TokenStream { let input: DeriveInput = syn::parse(input) .expect("failed to parse input"); @@ -91,8 +95,16 @@ pub fn derive_try_enum_to_string(input: TokenStream) -> TokenStream { for variant in parsed_enum.variants.iter() { match variant.match_variant( - |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) + ) + }), ) { Ok(Some((pattern, result))) => { match_patterns.push(pattern); @@ -123,6 +135,119 @@ pub fn derive_try_enum_to_string(input: TokenStream) -> TokenStream { }).into() } +#[proc_macro_derive(ScribeCowStr, attributes(enumscribe))] +pub fn derive_scribe_cow_str(input: TokenStream) -> TokenStream { + 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)); + + let enum_ident = &input.ident; + let enum_idents = iter::repeat(enum_ident); + + let mut match_patterns = Vec::with_capacity(parsed_enum.variants.len()); + let mut match_results = Vec::with_capacity(parsed_enum.variants.len()); + + for variant in parsed_enum.variants.iter() { + match variant.match_variant( + |name| Ok(quote! { + ::std::borrow::Cow::Borrowed(#name) + }), + |field| Ok(quote! { + ::std::borrow::Cow::Owned( + <_ as ::std::convert::Into<::std::string::String>>::into(#field) + ) + }), + ) { + Ok(Some((pattern, result))) => { + match_patterns.push(pattern); + match_results.push(result); + }, + + Ok(None) => return 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.to_string(), variant.data.ident.to_string(), IGNORE, + variant.data.ident.to_string(), + ), variant.span).into(), + + Err(err) => return err.into() + } + } + + (quote! { + impl ::enumscribe::ScribeCowStr for #enum_ident { + fn scribe(&self) -> ::std::borrow::Cow<'static, str> { + match self { + #(#enum_idents::#match_patterns => #match_results,)* + } + } + } + }).into() +} + +#[proc_macro_derive(TryScribeCowStr, attributes(enumscribe))] +pub fn derive_try_scribe_cow_str(input: TokenStream) -> TokenStream { + 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)); + + let enum_ident = &input.ident; + let enum_idents = iter::repeat(enum_ident); + + let mut ignore_variant = false; + let mut match_patterns = Vec::with_capacity(parsed_enum.variants.len()); + let mut match_results = Vec::with_capacity(parsed_enum.variants.len()); + + for variant in parsed_enum.variants.iter() { + match variant.match_variant( + |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) + ) + ) + }), + ) { + Ok(Some((pattern, result))) => { + match_patterns.push(pattern); + match_results.push(result); + }, + + Ok(None) => ignore_variant = true, + + Err(err) => return err.into() + } + } + + let ignore_arm = if ignore_variant { + quote! { _ => ::std::option::Option::None, } + } else { + quote! {} + }; + + (quote! { + impl ::enumscribe::TryScribeCowStr for #enum_ident { + fn try_scribe(&self) -> ::std::option::Option<::std::borrow::Cow<'static, str>> { + match self { + #(#enum_idents::#match_patterns => #match_results,)* + #ignore_arm + } + } + } + }).into() +} + fn get_enum_data(input: &DeriveInput) -> MacroResult<&DataEnum> { let enum_data = match &input.data { Data::Enum(enum_data) => enum_data, diff --git a/enumscribe_examples/examples/airports.rs b/enumscribe_examples/examples/airports.rs index ceb45c5..f770d66 100644 --- a/enumscribe_examples/examples/airports.rs +++ b/enumscribe_examples/examples/airports.rs @@ -1,7 +1,8 @@ use enumscribe::*; // #[derive(ScribeString)] -#[derive(TryScribeString)] + +#[derive(TryScribeCowStr)] enum Airport { #[enumscribe(str = "LHR", case_insensitive)] Heathrow, @@ -17,6 +18,8 @@ enum Airport { fn main() { let luton = Airport::Luton; - let luton_string = luton.try_scribe(); - println!("Hello, {:?}!", luton_string); + println!("Hello, {:?}!", luton.try_scribe()); + + let other = Airport::Other("Dedicated EasyJet-only airport".to_owned()); + println!("Hello, {:?}!", other.try_scribe()); }