From 1fab354030bc8910fb6f2c3696cfc37a1dcf0eca Mon Sep 17 00:00:00 2001 From: Pantonshire Date: Thu, 20 May 2021 11:51:19 +0100 Subject: [PATCH] Duplicate name checking now takes case insensitivity into account --- enumscribe_derive/src/enums.rs | 30 ++++++++++++++++---- enumscribe_derive/src/error.rs | 17 ++++++----- enumscribe_derive/src/lib.rs | 52 ++++++++++++++++++++++++---------- 3 files changed, 71 insertions(+), 28 deletions(-) diff --git a/enumscribe_derive/src/enums.rs b/enumscribe_derive/src/enums.rs index dbae8f8..f166fdd 100644 --- a/enumscribe_derive/src/enums.rs +++ b/enumscribe_derive/src/enums.rs @@ -1,11 +1,12 @@ use std::collections::HashSet; -use proc_macro2::{Ident, Span, TokenStream}; +use proc_macro2::{Ident, Span}; use quote::{quote, ToTokens}; use syn::{DataEnum, Fields}; use syn::spanned::Spanned; use crate::{CASE_INSENSITIVE, CRATE_ATTR, IGNORE, NAME, OTHER}; +use crate::TokenStream2; use crate::attribute::{Dict, Value}; use crate::error::{MacroError, MacroResult}; @@ -47,10 +48,10 @@ impl<'a> Variant<'a> { enum_ident: &Ident, named_fn: &F, other_fn: &G - ) -> MacroResult> + ) -> MacroResult> where - F: Fn(&Variant, &Ident, &str) -> MacroResult, - G: Fn(&Variant, &Ident, TokenStream) -> MacroResult + F: Fn(&Variant, &Ident, &str) -> MacroResult, + G: Fn(&Variant, &Ident, TokenStream2) -> MacroResult { let variant_ident = &self.data.ident; @@ -84,6 +85,8 @@ impl<'a> Variant<'a> { pub(crate) fn parse_enum(data: &DataEnum) -> MacroResult { let mut variants = Vec::with_capacity(data.variants.len()); let mut taken_names = HashSet::new(); + let mut taken_insensitive_names = HashSet::new(); + let mut taken_sensitive_names = HashSet::new(); let mut other_variant = false; for variant in data.variants.iter() { @@ -167,6 +170,23 @@ pub(crate) fn parse_enum(data: &DataEnum) -> MacroResult { )); } + taken_names.insert(name.clone()); + + // 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)) { + return Err(MacroError::new( + format!("duplicate name \"{}\"", name), + name_span, + )); + } + + if case_insensitive { + taken_insensitive_names.insert(lowercase_name); + } else { + taken_sensitive_names.insert(lowercase_name); + } + // Return an error if the variant has any fields if variant.fields.len() != 0 { let variant_ident = variant.ident.to_string(); @@ -189,8 +209,6 @@ pub(crate) fn parse_enum(data: &DataEnum) -> MacroResult { Fields::Unit => VariantConstructor::None, }; - taken_names.insert(name.clone()); - Variant { data: variant, v_type: VariantType::Named { name, constructor, case_insensitive }, diff --git a/enumscribe_derive/src/error.rs b/enumscribe_derive/src/error.rs index 1b1964a..b6b7941 100644 --- a/enumscribe_derive/src/error.rs +++ b/enumscribe_derive/src/error.rs @@ -1,12 +1,15 @@ +use proc_macro::TokenStream; use std::borrow::Cow; -use std::fmt; use std::error; +use std::fmt; use std::result; use proc_macro2::Span; use quote::quote_spanned; use syn::Error; +use crate::TokenStream2; + #[derive(Clone, Debug)] pub(crate) struct MacroError { pub(crate) message: Cow<'static, str>, @@ -16,18 +19,18 @@ 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, } } - pub(crate) fn to_token_stream(&self) -> proc_macro::TokenStream { + pub(crate) fn to_token_stream(&self) -> TokenStream { self.to_token_stream2().into() } - pub(crate) fn to_token_stream2(&self) -> proc_macro2::TokenStream { + pub(crate) fn to_token_stream2(&self) -> TokenStream2 { let message = &self.message; quote_spanned! { self.span => ::std::compile_error!(#message); @@ -41,13 +44,13 @@ impl From for MacroError { } } -impl From for proc_macro::TokenStream { +impl From for TokenStream { fn from(err: MacroError) -> Self { err.to_token_stream() } } -impl From for proc_macro2::TokenStream { +impl From for TokenStream2 { fn from(err: MacroError) -> Self { err.to_token_stream2() } @@ -63,7 +66,7 @@ impl error::Error for MacroError {} #[derive(Clone, Debug)] pub(crate) struct ValueTypeError { - pub(crate) message: Cow<'static, str> + pub(crate) message: Cow<'static, str>, } pub(crate) type ValueTypeResult = result::Result; diff --git a/enumscribe_derive/src/lib.rs b/enumscribe_derive/src/lib.rs index 46b619c..ed87040 100644 --- a/enumscribe_derive/src/lib.rs +++ b/enumscribe_derive/src/lib.rs @@ -9,7 +9,7 @@ use syn::{Data, DataEnum, DeriveInput}; use error::{MacroError, MacroResult}; -use crate::enums::Variant; +use crate::enums::{Variant, VariantType}; use proc_macro2::Ident; mod enums; @@ -55,15 +55,11 @@ fn derive_scribe( let enum_ident = &input.ident; - let mut match_patterns = Vec::with_capacity(parsed_enum.variants.len()); - let mut match_results = Vec::with_capacity(parsed_enum.variants.len()); + let mut match_arms = Vec::with_capacity(parsed_enum.variants.len()); for variant in parsed_enum.variants.iter() { match variant.match_variant(enum_ident, &named_fn, &other_fn) { - Ok(Some((pattern, result))) => { - match_patterns.push(pattern); - match_results.push(result); - } + 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() } @@ -73,7 +69,7 @@ fn derive_scribe( impl #trait_ident for #enum_ident { fn scribe(&self) -> #trait_return_type { match self { - #(#match_patterns => #match_results,)* + #(#match_arms,)* } } } @@ -101,15 +97,11 @@ fn derive_try_scribe( let enum_ident = &input.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()); + let mut match_arms = Vec::with_capacity(parsed_enum.variants.len()); for variant in parsed_enum.variants.iter() { match variant.match_variant(enum_ident, &named_fn, &other_fn) { - Ok(Some((pattern, result))) => { - match_patterns.push(pattern); - match_results.push(result); - } + Ok(Some((pattern, result))) => match_arms.push(quote! { #pattern => #result }), Ok(None) => ignore_variant = true, Err(err) => return err.into() } @@ -125,7 +117,7 @@ fn derive_try_scribe( impl #trait_ident for #enum_ident { fn try_scribe(&self) -> #trait_return_type { match self { - #(#match_patterns => #match_results,)* + #(#match_arms,)* #ignore_arm } } @@ -290,6 +282,36 @@ pub fn derive_try_scribe_cow_str(input: TokenStream) -> TokenStream { ) } +#[proc_macro_derive(Unscribe, attributes(enumscribe))] +pub fn derive_unscribe(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 mut other_arm = None; + // let mut case_sensitive_arms = Vec::new(); + // let mut case_insensitive_arms = Vec::new(); + // + // for variant in parsed_enum.variants.iter() { + // match variant.v_type { + // VariantType::Ignore => {} + // VariantType::Named { .. } => {} + // VariantType::Other { .. } => {} + // } + // } + + todo!() +} + +#[proc_macro_derive(TryUnscribe, attributes(enumscribe))] +pub fn derive_try_unscribe(input: TokenStream) -> TokenStream { + todo!() +} + fn get_enum_data(input: &DeriveInput) -> MacroResult<&DataEnum> { let enum_data = match &input.data { Data::Enum(enum_data) => enum_data,