///
/// 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:
/// # 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 {
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);
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;
}
}
- 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 {
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")