]> git.lizzy.rs Git - enumset.git/commitdiff
Implement #[enumset_no_ops]. Closes #2.
authorLymia Aluysia <lymia@lymiahugs.com>
Thu, 8 Nov 2018 10:55:01 +0000 (04:55 -0600)
committerLymia Aluysia <lymia@lymiahugs.com>
Thu, 8 Nov 2018 10:55:01 +0000 (04:55 -0600)
enumset/src/lib.rs
enumset_derive/src/lib.rs

index 06b1c0ae24c79e01751daf39ba260c29aabd9bef..4ff37284df82d49189c3b82319737f46ae8c6fec 100644 (file)
@@ -126,6 +126,16 @@ use private::EnumSetTypeRepr;
 ///
 /// In most cases, this trait should be implemented using `#[derive(EnumSetType)]`.
 ///
+/// # Custom Derive
+///
+/// The custom derive for `EnumSetType` automatically creates implementations of [`PartialEq`],
+/// [`Sub`], [`BitAnd`], [`BitOr`], [`BitXor`], and [`Not`] allowing the enum to be used as
+/// if it were an [`EnumSet`] in expressions. This can be disabled by adding an `#[enumset_no_ops]`
+/// annotation to the enum.
+///
+/// Any C-like enum is supported, as long as there are no more than 128 variants in the enum,
+/// and no variant discriminator is larger than 127.
+///
 /// # Examples
 ///
 /// Deriving a plain EnumSetType:
@@ -144,7 +154,18 @@ use private::EnumSetTypeRepr;
 /// # use enumset::*;
 /// #[derive(EnumSetType, Copy, Clone, Debug)]
 /// pub enum SparseEnum {
-///    A = 10, B = 20, C = 30,
+///    A = 10, B = 20, C = 30, D = 127,
+/// }
+/// ```
+///
+/// Deriving an EnumSetType without adding ops:
+///
+/// ```rust
+/// # use enumset::*;
+/// #[derive(EnumSetType, Copy, Clone, Debug)]
+/// #[enumset_no_ops]
+/// pub enum NoOpsEnum {
+///    A, B, C, D, E, F, G,
 /// }
 /// ```
 pub unsafe trait EnumSetType: Copy {
index c8144e9725047a85f071987cf71814dc0c22f68c..1238f748fb8070be4e15229887263ce06c13fe89 100644 (file)
@@ -25,7 +25,9 @@ fn error(_: Span, data: &str) -> TokenStream {
     panic!("{}", data)
 }
 
-fn enum_set_type_impl(name: &Ident, all_variants: u128, repr: Ident) -> SynTokenStream {
+fn enum_set_type_impl(
+    name: &Ident, all_variants: u128, repr: Ident, no_ops: bool,
+) -> SynTokenStream {
     let typed_enumset = quote!(::enumset::EnumSet<#name>);
     let core = quote!(::enumset::internal::core);
 
@@ -33,6 +35,48 @@ fn enum_set_type_impl(name: &Ident, all_variants: u128, repr: Ident) -> SynToken
     let all_variants_tt = TokenTree::Literal(Literal::u128_unsuffixed(all_variants));
     let all_variants_tt = SynTokenStream::from(TokenStream::from(all_variants_tt));
 
+    let ops = if no_ops {
+        quote! {}
+    } else {
+        quote! {
+            impl <O : Into<#typed_enumset>> #core::ops::Sub<O> for #name {
+                type Output = #typed_enumset;
+                fn sub(self, other: O) -> Self::Output {
+                    ::enumset::EnumSet::only(self) - other.into()
+                }
+            }
+            impl <O : Into<#typed_enumset>> #core::ops::BitAnd<O> for #name {
+                type Output = #typed_enumset;
+                fn bitand(self, other: O) -> Self::Output {
+                    ::enumset::EnumSet::only(self) & other.into()
+                }
+            }
+            impl <O : Into<#typed_enumset>> #core::ops::BitOr<O> for #name {
+                type Output = #typed_enumset;
+                fn bitor(self, other: O) -> Self::Output {
+                    ::enumset::EnumSet::only(self) | other.into()
+                }
+            }
+            impl <O : Into<#typed_enumset>> #core::ops::BitXor<O> for #name {
+                type Output = #typed_enumset;
+                fn bitxor(self, other: O) -> Self::Output {
+                    ::enumset::EnumSet::only(self) ^ other.into()
+                }
+            }
+            impl #core::ops::Not for #name {
+                type Output = #typed_enumset;
+                fn not(self) -> Self::Output {
+                    !::enumset::EnumSet::only(self)
+                }
+            }
+            impl #core::cmp::PartialEq<#typed_enumset> for #name {
+                fn eq(&self, other: &#typed_enumset) -> bool {
+                    ::enumset::EnumSet::only(*self) == *other
+                }
+            }
+        }
+    };
+
     quote! {
         unsafe impl ::enumset::EnumSetType for #name {
             type Repr = #repr;
@@ -46,45 +90,11 @@ fn enum_set_type_impl(name: &Ident, all_variants: u128, repr: Ident) -> SynToken
             }
         }
 
-        impl <O : Into<#typed_enumset>> #core::ops::Sub<O> for #name {
-            type Output = #typed_enumset;
-            fn sub(self, other: O) -> Self::Output {
-                ::enumset::EnumSet::only(self) - other.into()
-            }
-        }
-        impl <O : Into<#typed_enumset>> #core::ops::BitAnd<O> for #name {
-            type Output = #typed_enumset;
-            fn bitand(self, other: O) -> Self::Output {
-                ::enumset::EnumSet::only(self) & other.into()
-            }
-        }
-        impl <O : Into<#typed_enumset>> #core::ops::BitOr<O> for #name {
-            type Output = #typed_enumset;
-            fn bitor(self, other: O) -> Self::Output {
-                ::enumset::EnumSet::only(self) | other.into()
-            }
-        }
-        impl <O : Into<#typed_enumset>> #core::ops::BitXor<O> for #name {
-            type Output = #typed_enumset;
-            fn bitxor(self, other: O) -> Self::Output {
-                ::enumset::EnumSet::only(self) ^ other.into()
-            }
-        }
-        impl #core::ops::Not for #name {
-            type Output = #typed_enumset;
-            fn not(self) -> Self::Output {
-                !::enumset::EnumSet::only(self)
-            }
-        }
-        impl #core::cmp::PartialEq<#typed_enumset> for #name {
-            fn eq(&self, other: &#typed_enumset) -> bool {
-                ::enumset::EnumSet::only(*self) == *other
-            }
-        }
+        #ops
     }
 }
 
-#[proc_macro_derive(EnumSetType)]
+#[proc_macro_derive(EnumSetType, attributes(enumset_no_ops))]
 pub fn derive_enum_set_type(input: TokenStream) -> TokenStream {
     let input: DeriveInput = parse_macro_input!(input);
     if let Data::Enum(data) = input.data {
@@ -147,7 +157,20 @@ pub fn derive_enum_set_type(input: TokenStream) -> TokenStream {
                 panic!("max_variant > 128?")
             }, Span::call_site());
 
-            enum_set_type_impl(&input.ident, all_variants, repr).into()
+            let excluded_impl_attrs: Vec<_> = input.attrs.iter().filter(|attr| match attr {
+                Attribute { path: Path { segments, .. }, .. } =>
+                    segments.len() == 1 &&
+                    segments[0].ident.to_string() == "enumset_no_ops"
+            }).collect();
+            for attr in &excluded_impl_attrs {
+                if !attr.tts.is_empty() {
+                    return error(attr.span(), "`#[enumset_no_ops]` takes no arguments.")
+                }
+            }
+
+            enum_set_type_impl(
+                &input.ident, all_variants, repr, !excluded_impl_attrs.is_empty(),
+            ).into()
         }
     } else {
         error(input.span(), "`#[derive(EnumSetType)]` may only be used on enums")