rename
Pantonshire 5 years ago
parent fce589369b
commit ed83ffc5f3

@ -116,10 +116,10 @@ pub trait TryScribeCowStr {
fn try_scribe(&self) -> Option<Cow<'static, str>>;
}
pub trait Unscribe {
pub trait Unscribe: Sized {
fn unscribe(to_unscribe: &str) -> Self;
}
pub trait TryUnscribe {
pub trait TryUnscribe: Sized {
fn try_unscribe(to_unscribe: &str) -> Option<Self>;
}

@ -42,10 +42,15 @@ pub(crate) enum VariantConstructor {
}
impl<'a> Variant<'a> {
pub(crate) fn match_variant<F, G>(&self, named_fn: F, other_fn: G) -> MacroResult<Option<(TokenStream, TokenStream)>>
pub(crate) fn match_variant<F, G>(
&self,
enum_ident: &Ident,
named_fn: &F,
other_fn: &G
) -> MacroResult<Option<(TokenStream, TokenStream)>>
where
F: Fn(&str) -> MacroResult<TokenStream>,
G: Fn(TokenStream) -> MacroResult<TokenStream>
F: Fn(&Variant, &Ident, &str) -> MacroResult<TokenStream>,
G: Fn(&Variant, &Ident, TokenStream) -> MacroResult<TokenStream>
{
let variant_ident = &self.data.ident;
@ -54,11 +59,11 @@ impl<'a> Variant<'a> {
VariantType::Named { name, constructor, .. } => {
let pattern = match constructor {
VariantConstructor::None => quote! { #variant_ident },
VariantConstructor::Paren => quote! { #variant_ident () },
VariantConstructor::Brace => quote! { #variant_ident {} }
VariantConstructor::None => quote! { #enum_ident::#variant_ident },
VariantConstructor::Paren => quote! { #enum_ident::#variant_ident() },
VariantConstructor::Brace => quote! { #enum_ident::#variant_ident{} }
};
Ok(Some((pattern, named_fn(name)?)))
Ok(Some((pattern, named_fn(self, enum_ident, name)?)))
}
VariantType::Other { field_name } => {
@ -67,10 +72,10 @@ impl<'a> Variant<'a> {
None => quote! { __enumscribe_other_inner }
};
let pattern = match field_name {
Some(_) => quote! { #variant_ident { #field_name_tokens } },
None => quote! { #variant_ident (#field_name_tokens) }
Some(_) => quote! { #enum_ident::#variant_ident{#field_name_tokens} },
None => quote! { #enum_ident::#variant_ident(#field_name_tokens) }
};
Ok(Some((pattern, other_fn(field_name_tokens)?)))
Ok(Some((pattern, other_fn(self, enum_ident, field_name_tokens)?)))
}
}
}

@ -9,6 +9,9 @@ use syn::{Data, DataEnum, DeriveInput};
use error::{MacroError, MacroResult};
use crate::enums::Variant;
use proc_macro2::Ident;
mod enums;
mod attribute;
mod error;
@ -20,6 +23,8 @@ const OTHER: &'static str = "other";
const IGNORE: &'static str = "ignore";
const CASE_INSENSITIVE: &'static str = "case_insensitive";
type TokenStream2 = proc_macro2::TokenStream;
macro_rules! proc_try {
($x:expr) => {
match $x {
@ -29,8 +34,19 @@ macro_rules! proc_try {
};
}
#[proc_macro_derive(ScribeStaticStr, attributes(enumscribe))]
pub fn derive_scribe_static_str(input: TokenStream) -> TokenStream {
fn derive_scribe<F, G, E>(
input: TokenStream,
trait_ident: TokenStream2,
trait_return_type: TokenStream2,
named_fn: F,
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
{
let input: DeriveInput = syn::parse(input)
.expect("failed to parse input");
@ -38,54 +54,44 @@ pub fn derive_scribe_static_str(input: TokenStream) -> TokenStream {
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! {
#name
}),
|_| 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.to_string(), variant.data.ident.to_string(), OTHER
), variant.span)),
) {
match variant.match_variant(enum_ident, &named_fn, &other_fn) {
Ok(Some((pattern, result))) => {
match_patterns.push(pattern);
match_results.push(result);
},
Ok(None) => return 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.to_string(), variant.data.ident.to_string(), IGNORE,
variant.data.ident.to_string(),
), variant.span).into(),
}
Ok(None) => return ignore_err_fn(variant, enum_ident).into(),
Err(err) => return err.into()
}
}
(quote! {
impl ::enumscribe::ScribeStaticStr for #enum_ident {
fn scribe(&self) -> &'static str {
impl #trait_ident for #enum_ident {
fn scribe(&self) -> #trait_return_type {
match self {
#(#enum_idents::#match_patterns => #match_results,)*
#(#match_patterns => #match_results,)*
}
}
}
}).into()
}
#[proc_macro_derive(TryScribeStaticStr, attributes(enumscribe))]
pub fn derive_try_scribe_static_str(input: TokenStream) -> TokenStream {
fn derive_try_scribe<F, G>(
input: TokenStream,
trait_ident: TokenStream2,
trait_return_type: TokenStream2,
named_fn: F,
other_fn: G,
ignore_result: TokenStream2
) -> TokenStream
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");
@ -93,46 +99,33 @@ pub fn derive_try_scribe_static_str(input: TokenStream) -> TokenStream {
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(#name)
}),
|_| 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 ScribeCowStr instead",
enum_ident.to_string(), variant.data.ident.to_string(), OTHER
), variant.span)),
) {
match variant.match_variant(enum_ident, &named_fn, &other_fn) {
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, }
quote! { _ => #ignore_result, }
} else {
quote! {}
};
(quote! {
impl ::enumscribe::TryScribeStaticStr for #enum_ident {
fn try_scribe(&self) -> ::std::option::Option<&'static str> {
impl #trait_ident for #enum_ident {
fn try_scribe(&self) -> #trait_return_type {
match self {
#(#enum_idents::#match_patterns => #match_results,)*
#(#match_patterns => #match_results,)*
#ignore_arm
}
}
@ -140,226 +133,161 @@ pub fn derive_try_scribe_static_str(input: TokenStream) -> TokenStream {
}).into()
}
#[proc_macro_derive(ScribeString, attributes(enumscribe))]
pub fn derive_scribe_string(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));
#[proc_macro_derive(ScribeStaticStr, attributes(enumscribe))]
pub fn derive_scribe_static_str(input: TokenStream) -> TokenStream {
derive_scribe(
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.to_string(), variant.data.ident.to_string(), 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.to_string(), variant.data.ident.to_string(), IGNORE,
variant.data.ident.to_string(),
), variant.span)
)
}
let enum_ident = &input.ident;
let enum_idents = iter::repeat(enum_ident);
#[proc_macro_derive(TryScribeStaticStr, attributes(enumscribe))]
pub fn derive_try_scribe_static_str(input: TokenStream) -> TokenStream {
derive_try_scribe(
input,
let mut match_patterns = Vec::with_capacity(parsed_enum.variants.len());
let mut match_results = Vec::with_capacity(parsed_enum.variants.len());
quote! { ::enumscribe::TryScribeStaticStr },
quote! { ::std::option::Option<&'static str> },
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)
}),
) {
Ok(Some((pattern, result))) => {
match_patterns.push(pattern);
match_results.push(result);
},
|_, _, name| Ok(quote! {
::std::option::Option::Some(#name)
}),
Ok(None) => return 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.to_string(), variant.data.ident.to_string(), IGNORE,
variant.data.ident.to_string(),
), variant.span).into(),
|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.to_string(), variant.data.ident.to_string(), OTHER
), variant.span)),
Err(err) => return err.into()
}
}
quote! { ::std::option::Option::None }
)
}
(quote! {
impl ::enumscribe::ScribeString for #enum_ident {
fn scribe(&self) -> ::std::string::String {
match self {
#(#enum_idents::#match_patterns => #match_results,)*
}
}
}
}).into()
#[proc_macro_derive(ScribeString, attributes(enumscribe))]
pub fn derive_scribe_string(input: TokenStream) -> TokenStream {
derive_scribe(
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.to_string(), variant.data.ident.to_string(), IGNORE,
variant.data.ident.to_string(),
), variant.span)
)
}
#[proc_macro_derive(TryScribeString, attributes(enumscribe))]
pub fn derive_try_scribe_string(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(
<_ 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);
match_results.push(result);
},
derive_try_scribe(
input,
Ok(None) => ignore_variant = true,
quote! { ::enumscribe::TryScribeString },
quote! { ::std::option::Option<::std::string::String> },
Err(err) => return err.into()
}
}
|_, _, name| Ok(quote! {
::std::option::Option::Some(
<_ as ::std::borrow::ToOwned>::to_owned(#name)
)
}),
let ignore_arm = if ignore_variant {
quote! { _ => ::std::option::Option::None, }
} else {
quote! {}
};
|_, _, field| Ok(quote! {
::std::option::Option::Some(
<_ as ::std::convert::Into<::std::string::String>>::into(#field)
)
}),
(quote! {
impl ::enumscribe::TryScribeString for #enum_ident {
fn try_scribe(&self) -> ::std::option::Option<::std::string::String> {
match self {
#(#enum_idents::#match_patterns => #match_results,)*
#ignore_arm
}
}
}
}).into()
quote! { ::std::option::Option::None }
)
}
#[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());
derive_scribe(
input,
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(),
quote! { ::enumscribe::ScribeCowStr },
quote! { ::std::borrow::Cow<'static, str> },
Err(err) => return err.into()
}
}
|_, _, name| Ok(quote! {
::std::borrow::Cow::Borrowed(#name)
}),
(quote! {
impl ::enumscribe::ScribeCowStr for #enum_ident {
fn scribe(&self) -> ::std::borrow::Cow<'static, str> {
match self {
#(#enum_idents::#match_patterns => #match_results,)*
}
}
}
}).into()
|_, _, 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.to_string(), variant.data.ident.to_string(), IGNORE,
variant.data.ident.to_string(),
), variant.span)
)
}
#[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");
derive_try_scribe(
input,
let enum_data = proc_try!(get_enum_data(&input));
let parsed_enum = proc_try!(enums::parse_enum(enum_data));
quote! { ::enumscribe::TryScribeCowStr },
quote! { ::std::option::Option<::std::borrow::Cow<'static, str>> },
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());
|_, _, name| Ok(quote! {
::std::option::Option::Some(
::std::borrow::Cow::Borrowed(#name)
)
}),
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)
)
|_, _, 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()
quote! { ::std::option::Option::None }
)
}
fn get_enum_data(input: &DeriveInput) -> MacroResult<&DataEnum> {

@ -1,8 +1,6 @@
use enumscribe::*;
// #[derive(ScribeString)]
#[derive(TryScribeStaticStr)]
#[derive(TryScribeCowStr)]
enum Airport {
#[enumscribe(str = "LHR", case_insensitive)]
Heathrow,
@ -12,14 +10,14 @@ enum Airport {
Luton,
#[enumscribe(str = "BHX", case_insensitive, ignore)]
BirminghamInternational,
// #[enumscribe(other)]
// Other(String),
#[enumscribe(other)]
Other(String),
}
fn main() {
let luton = Airport::Luton;
println!("Hello, {:?}!", luton.try_scribe());
// let other = Airport::Other("Dedicated EasyJet-only airport".to_owned());
// println!("Hello, {:?}!", other.try_scribe());
let other = Airport::Other("Dedicated EasyJet-only airport".to_owned());
println!("Hello, {:?}!", other.try_scribe());
}

Loading…
Cancel
Save