X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=enumset_derive%2Fsrc%2Flib.rs;h=88ac367e0ad914cf317bc046bebc9a51e1c655e2;hb=45164a98b502ffe04120c2bbe044f69b6c3fe3e9;hp=993ccc071af965edfb2bfdbff0b37019dc8b4f96;hpb=036e9cae7fea351e5e422f5e164025d09039db9e;p=enumset.git diff --git a/enumset_derive/src/lib.rs b/enumset_derive/src/lib.rs index 993ccc0..88ac367 100644 --- a/enumset_derive/src/lib.rs +++ b/enumset_derive/src/lib.rs @@ -1,17 +1,17 @@ -#![recursion_limit="256"] +#![recursion_limit = "256"] extern crate proc_macro; use darling::*; use proc_macro::TokenStream; -use proc_macro2::{TokenStream as SynTokenStream, Literal, Span}; -use std::collections::HashSet; -use syn::{*, Result, Error}; -use syn::spanned::Spanned; +use proc_macro2::{Literal, Span, TokenStream as SynTokenStream}; use quote::*; +use std::{collections::HashSet, fmt::Display}; +use syn::spanned::Spanned; +use syn::{Error, Result, *}; /// Helper function for emitting compile errors. -fn error(span: Span, message: &str) -> Result { +fn error(span: Span, message: impl Display) -> Result { Err(Error::new(span, message)) } @@ -21,6 +21,8 @@ fn error(span: Span, message: &str) -> Result { struct EnumsetAttrs { no_ops: bool, no_super_impls: bool, + #[darling(default)] + repr: Option, serialize_as_list: bool, serialize_deny_unknown: bool, #[darling(default)] @@ -44,6 +46,8 @@ struct EnumSetInfo { name: Ident, /// The crate name to use. crate_name: Option, + /// The numeric type to represent the `EnumSet` as in memory. + explicit_mem_repr: Option, /// The numeric type to serialize the enum as. explicit_serde_repr: Option, /// Whether the underlying repr of the enum supports negative values. @@ -76,7 +80,10 @@ impl EnumSetInfo { EnumSetInfo { name: input.ident.clone(), crate_name: attrs.crate_name.map(|x| Ident::new(&x, Span::call_site())), - explicit_serde_repr: attrs.serialize_repr.map(|x| Ident::new(&x, Span::call_site())), + explicit_mem_repr: attrs.repr.map(|x| Ident::new(&x, Span::call_site())), + explicit_serde_repr: attrs + .serialize_repr + .map(|x| Ident::new(&x, Span::call_site())), has_signed_repr: false, has_large_repr: false, variants: Vec::new(), @@ -87,7 +94,7 @@ impl EnumSetInfo { no_ops: attrs.no_ops, no_super_impls: attrs.no_super_impls, serialize_as_list: attrs.serialize_as_list, - serialize_deny_unknown: attrs.serialize_deny_unknown + serialize_deny_unknown: attrs.serialize_deny_unknown, } } @@ -110,7 +117,7 @@ impl EnumSetInfo { self.has_large_repr = true; Ok(()) } - _ => error(attr_span, "Unsupported repr.") + _ => error(attr_span, "Unsupported repr."), } } /// Adds a variant to the enumset. @@ -166,10 +173,8 @@ impl EnumSetInfo { if discriminant > self.max_discrim { self.max_discrim = discriminant; } - self.variants.push(EnumSetValue { - name: variant.ident.clone(), - variant_repr: discriminant, - }); + self.variants + .push(EnumSetValue { name: variant.ident.clone(), variant_repr: discriminant }); self.used_variant_names.insert(variant.ident.to_string()); self.used_discriminants.insert(discriminant); @@ -180,29 +185,44 @@ impl EnumSetInfo { } /// Validate the enumset type. fn validate(&self) -> Result<()> { - // Check if all bits of the bitset can fit in the serialization representation. - if let Some(explicit_serde_repr) = &self.explicit_serde_repr { - let is_overflowed = match explicit_serde_repr.to_string().as_str() { - "u8" => self.max_discrim >= 8, - "u16" => self.max_discrim >= 16, - "u32" => self.max_discrim >= 32, - "u64" => self.max_discrim >= 64, - "u128" => self.max_discrim >= 128, + fn do_check(ty: &str, max_discrim: u32, what: &str) -> Result<()> { + let is_overflowed = match ty { + "u8" => max_discrim >= 8, + "u16" => max_discrim >= 16, + "u32" => max_discrim >= 32, + "u64" => max_discrim >= 64, + "u128" => max_discrim >= 128, _ => error( Span::call_site(), - "Only `u8`, `u16`, `u32`, `u64` and `u128` are supported for serde_repr." + format!( + "Only `u8`, `u16`, `u32`, `u64` and `u128` are supported for {}.", + what + ), )?, }; if is_overflowed { - error(Span::call_site(), "serialize_repr cannot be smaller than bitset.")?; + error(Span::call_site(), format!("{} cannot be smaller than bitset.", what))?; } + Ok(()) + } + + // Check if all bits of the bitset can fit in the serialization representation. + if let Some(explicit_serde_repr) = &self.explicit_serde_repr { + do_check(&explicit_serde_repr.to_string(), self.max_discrim, "serialize_repr")?; + } + + // Check if all bits of the bitset can fit in the memory representation, if one was given. + if let Some(explicit_mem_repr) = &self.explicit_mem_repr { + do_check(&explicit_mem_repr.to_string(), self.max_discrim, "repr")?; } Ok(()) } /// Computes the underlying type used to store the enumset. fn enumset_repr(&self) -> SynTokenStream { - if self.max_discrim <= 7 { + if let Some(explicit_mem_repr) = &self.explicit_mem_repr { + explicit_mem_repr.to_token_stream() + } else if self.max_discrim <= 7 { quote! { u8 } } else if self.max_discrim <= 15 { quote! { u16 } @@ -240,9 +260,29 @@ impl EnumSetInfo { /// Generates the actual `EnumSetType` impl. fn enum_set_type_impl(info: EnumSetInfo) -> SynTokenStream { let name = &info.name; + let enumset = match &info.crate_name { Some(crate_name) => quote!(::#crate_name), - None => quote!(::enumset), + None => { + #[cfg(feature = "proc-macro-crate")] + { + use proc_macro_crate::FoundCrate; + + let crate_name = proc_macro_crate::crate_name("enumset"); + match crate_name { + Ok(FoundCrate::Name(name)) => { + let ident = Ident::new(&name, Span::call_site()); + quote!(::#ident) + } + _ => quote!(::enumset), + } + } + + #[cfg(not(feature = "proc-macro-crate"))] + { + quote!(::enumset) + } + } }; let typed_enumset = quote!(#enumset::EnumSet<#name>); let core = quote!(#enumset::__internal::core_export); @@ -292,7 +332,6 @@ fn enum_set_type_impl(info: EnumSetInfo) -> SynTokenStream { } }; - #[cfg(feature = "serde")] let serde = quote!(#enumset::__internal::serde); @@ -348,7 +387,7 @@ fn enum_set_type_impl(info: EnumSetInfo) -> SynTokenStream { } } } else { - quote! { } + quote! {} }; quote! { fn serialize( @@ -369,7 +408,7 @@ fn enum_set_type_impl(info: EnumSetInfo) -> SynTokenStream { }; #[cfg(not(feature = "serde"))] - let serde_ops = quote! { }; + let serde_ops = quote! {}; let is_uninhabited = info.variants.is_empty(); let is_zst = info.variants.len() == 1; @@ -397,9 +436,13 @@ fn enum_set_type_impl(info: EnumSetInfo) -> SynTokenStream { let variant_value: Vec<_> = info.variants.iter().map(|x| x.variant_repr).collect(); let const_field: Vec<_> = ["IS_U8", "IS_U16", "IS_U32", "IS_U64", "IS_U128"] - .iter().map(|x| Ident::new(x, Span::call_site())).collect(); + .iter() + .map(|x| Ident::new(x, Span::call_site())) + .collect(); let int_type: Vec<_> = ["u8", "u16", "u32", "u64", "u128"] - .iter().map(|x| Ident::new(x, Span::call_site())).collect(); + .iter() + .map(|x| Ident::new(x, Span::call_site())) + .collect(); quote! { fn enum_into_u32(self) -> u32 { @@ -462,6 +505,16 @@ fn enum_set_type_impl(info: EnumSetInfo) -> SynTokenStream { } }; + let impl_with_repr = if info.explicit_mem_repr.is_some() { + quote! { + unsafe impl #enumset::EnumSetTypeWithRepr for #name { + type Repr = #repr; + } + } + } else { + quote! {} + }; + quote! { unsafe impl #enumset::__internal::EnumSetTypePrivate for #name { type Repr = #repr; @@ -472,6 +525,7 @@ fn enum_set_type_impl(info: EnumSetInfo) -> SynTokenStream { unsafe impl #enumset::EnumSetType for #name { } + #impl_with_repr #super_impls impl #name {