diff --git a/Cargo.toml b/Cargo.toml index dcd1ce0..4161de3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,9 @@ homepage = "https://github.com/contain-rs/enum-set" documentation = "https://contain-rs.github.io/enum-set/enum_set" keywords = ["data-structures"] readme = "README.md" + +[dev-dependencies] +enum-set-derive = { version = "0.1.0", path = "enum-set-derive" } + +[workspace] +members = ["enum-set-derive"] diff --git a/enum-set-derive/Cargo.toml b/enum-set-derive/Cargo.toml new file mode 100644 index 0000000..4bae33e --- /dev/null +++ b/enum-set-derive/Cargo.toml @@ -0,0 +1,22 @@ +[package] + +name = "enum-set-derive" +version = "0.1.0" +license = "MIT/Apache-2.0" +description = "Macros 1.1 implementation of #[derive(EnumMap)]" +authors = [ + "Konrad Borowski " +] + +repository = "https://github.com/contain-rs/enum-set" +homepage = "https://github.com/contain-rs/enum-set" +documentation = "https://contain-rs.github.io/enum-set/enum_set" +keywords = ["data-structures"] +readme = "README.md" + +[lib] +proc-macro = true + +[dependencies] +syn = "0.11.11" +quote = "0.3.15" diff --git a/enum-set-derive/src/lib.rs b/enum-set-derive/src/lib.rs new file mode 100644 index 0000000..cb1f113 --- /dev/null +++ b/enum-set-derive/src/lib.rs @@ -0,0 +1,66 @@ +extern crate proc_macro; +extern crate syn; +#[macro_use] +extern crate quote; + +use std::iter; + +use proc_macro::TokenStream; +use syn::{Body, Ident, Variant, VariantData}; +use quote::Tokens; + +fn generate_enum_code(name: &Ident, variants: &[Variant]) -> Tokens { + for (count, + &Variant { + ref data, + ref discriminant, + .. + }) in variants.iter().enumerate() + { + if count == 32 { + panic!("#[derive(CLike)] supports at most 32 variants"); + } + if data != &VariantData::Unit { + panic!("#[derive(CLike)] requires C style style enum"); + } + if discriminant.is_some() { + panic!("#[derive(CLike)] doesn't currently support discriminants"); + } + } + + let variant = variants.iter().map(|variant| &variant.ident); + let counter = 0..variants.len() as u32; + let names = iter::repeat(name); + + let to_u32 = if variants.len() == 0 { + quote! { unreachable!() } + } else { + quote! { *self as u32 } + }; + + quote! { + impl ::enum_set::CLike for #name { + unsafe fn from_u32(value: u32) -> Self { + match value { + #( + #counter => #names::#variant, + )* + _ => unreachable!() + } + } + fn to_u32(&self) -> u32 { + #to_u32 + } + } + } +} + +#[proc_macro_derive(CLike)] +pub fn derive_clike(input: TokenStream) -> TokenStream { + let input = syn::parse_macro_input(&input.to_string()).unwrap(); + match input.body { + Body::Enum(ref variants) => generate_enum_code(&input.ident, variants), + _ => panic!("#[derive(CLike)] is only defined for enums"), + }.parse() + .unwrap() +} diff --git a/src/lib.rs b/src/lib.rs index f76c18b..5ce59ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,24 +47,15 @@ impl hash::Hash for EnumSet { /// A typical implementation can be seen below: /// /// ``` -/// use enum_set::CLike; -/// use std::mem; +/// extern crate enum_set; +/// #[macro_use] +/// extern crate enum_set_derive; /// -/// #[derive(Clone, Copy)] -/// #[repr(u32)] +/// #[derive(Clone, Copy, CLike)] /// enum Foo { /// A, B, C /// } -/// -/// impl CLike for Foo { -/// fn to_u32(&self) -> u32 { -/// *self as u32 -/// } -/// -/// unsafe fn from_u32(v: u32) -> Foo { -/// mem::transmute(v) -/// } -/// } +/// # fn main() {} /// ``` pub trait CLike { /// Converts a C-like enum to a `u32`. The value must be `<= 31`. @@ -268,257 +259,3 @@ impl<'a, E: CLike> IntoIterator for &'a EnumSet { type IntoIter = Iter; fn into_iter(self) -> Iter { self.iter() } } - -#[cfg(test)] -mod tests { - use self::Foo::*; - use std::mem; - - use super::{EnumSet, CLike}; - - #[derive(Copy, Clone, PartialEq, Debug)] - #[repr(u32)] - enum Foo { - A, B, C - } - - impl CLike for Foo { - fn to_u32(&self) -> u32 { - *self as u32 - } - - unsafe fn from_u32(v: u32) -> Foo { - mem::transmute(v) - } - } - - #[test] - fn test_new() { - let e: EnumSet = EnumSet::new(); - assert!(e.is_empty()); - } - - #[test] - fn test_debug() { - let mut e = EnumSet::new(); - assert_eq!("{}", format!("{:?}", e)); - e.insert(A); - assert_eq!("{A}", format!("{:?}", e)); - e.insert(C); - assert_eq!("{A, C}", format!("{:?}", e)); - } - - #[test] - fn test_len() { - let mut e = EnumSet::new(); - assert_eq!(e.len(), 0); - e.insert(A); - e.insert(B); - e.insert(C); - assert_eq!(e.len(), 3); - e.remove(&A); - assert_eq!(e.len(), 2); - e.clear(); - assert_eq!(e.len(), 0); - } - - /////////////////////////////////////////////////////////////////////////// - // intersect - - #[test] - fn test_two_empties_do_not_intersect() { - let e1: EnumSet = EnumSet::new(); - let e2: EnumSet = EnumSet::new(); - assert!(e1.is_disjoint(&e2)); - } - - #[test] - fn test_empty_does_not_intersect_with_full() { - let e1: EnumSet = EnumSet::new(); - - let mut e2: EnumSet = EnumSet::new(); - e2.insert(A); - e2.insert(B); - e2.insert(C); - - assert!(e1.is_disjoint(&e2)); - } - - #[test] - fn test_disjoint_intersects() { - let mut e1: EnumSet = EnumSet::new(); - e1.insert(A); - - let mut e2: EnumSet = EnumSet::new(); - e2.insert(B); - - assert!(e1.is_disjoint(&e2)); - } - - #[test] - fn test_overlapping_intersects() { - let mut e1: EnumSet = EnumSet::new(); - e1.insert(A); - - let mut e2: EnumSet = EnumSet::new(); - e2.insert(A); - e2.insert(B); - - assert!(!e1.is_disjoint(&e2)); - } - - /////////////////////////////////////////////////////////////////////////// - // contains and contains_elem - - #[test] - fn test_superset() { - let mut e1: EnumSet = EnumSet::new(); - e1.insert(A); - - let mut e2: EnumSet = EnumSet::new(); - e2.insert(A); - e2.insert(B); - - let mut e3: EnumSet = EnumSet::new(); - e3.insert(C); - - assert!(e1.is_subset(&e2)); - assert!(e2.is_superset(&e1)); - assert!(!e3.is_superset(&e2)); - assert!(!e2.is_superset(&e3)); - } - - #[test] - fn test_contains() { - let mut e1: EnumSet = EnumSet::new(); - e1.insert(A); - assert!(e1.contains(&A)); - assert!(!e1.contains(&B)); - assert!(!e1.contains(&C)); - - e1.insert(A); - e1.insert(B); - assert!(e1.contains(&A)); - assert!(e1.contains(&B)); - assert!(!e1.contains(&C)); - } - - /////////////////////////////////////////////////////////////////////////// - // iter - - #[test] - fn test_iterator() { - let mut e1: EnumSet = EnumSet::new(); - - let elems: Vec = e1.iter().collect(); - assert!(elems.is_empty()); - - e1.insert(A); - let elems: Vec<_> = e1.iter().collect(); - assert_eq!(vec![A], elems); - - e1.insert(C); - let elems: Vec<_> = e1.iter().collect(); - assert_eq!(vec![A,C], elems); - - e1.insert(C); - let elems: Vec<_> = e1.iter().collect(); - assert_eq!(vec![A,C], elems); - - e1.insert(B); - let elems: Vec<_> = e1.iter().collect(); - assert_eq!(vec![A,B,C], elems); - } - - #[test] - fn test_clone_iterator() { - let mut e: EnumSet = EnumSet::new(); - e.insert(A); - e.insert(B); - e.insert(C); - - let mut iter1 = e.iter(); - let first_elem = iter1.next(); - assert_eq!(Some(A), first_elem); - - let iter2 = iter1.clone(); - let elems1: Vec<_> = iter1.collect(); - assert_eq!(vec![B, C], elems1); - - let elems2: Vec<_> = iter2.collect(); - assert_eq!(vec![B, C], elems2); - } - - /////////////////////////////////////////////////////////////////////////// - // operators - - #[test] - fn test_operators() { - let mut e1: EnumSet = EnumSet::new(); - e1.insert(A); - e1.insert(C); - - let mut e2: EnumSet = EnumSet::new(); - e2.insert(B); - e2.insert(C); - - let e_union = e1 | e2; - let elems: Vec<_> = e_union.iter().collect(); - assert_eq!(vec![A,B,C], elems); - - let e_intersection = e1 & e2; - let elems: Vec<_> = e_intersection.iter().collect(); - assert_eq!(vec![C], elems); - - // Another way to express intersection - let e_intersection = e1 - (e1 - e2); - let elems: Vec<_> = e_intersection.iter().collect(); - assert_eq!(vec![C], elems); - - let e_subtract = e1 - e2; - let elems: Vec<_> = e_subtract.iter().collect(); - assert_eq!(vec![A], elems); - - // Bitwise XOR of two sets, aka symmetric difference - let e_symmetric_diff = e1 ^ e2; - let elems: Vec<_> = e_symmetric_diff.iter().collect(); - assert_eq!(vec![A,B], elems); - - // Another way to express symmetric difference - let e_symmetric_diff = (e1 - e2) | (e2 - e1); - let elems: Vec<_> = e_symmetric_diff.iter().collect(); - assert_eq!(vec![A,B], elems); - - // Yet another way to express symmetric difference - let e_symmetric_diff = (e1 | e2) - (e1 & e2); - let elems: Vec<_> = e_symmetric_diff.iter().collect(); - assert_eq!(vec![A,B], elems); - } - - #[test] - #[should_panic] - fn test_overflow() { - #[allow(dead_code)] - #[repr(u32)] - #[derive(Clone, Copy)] - enum Bar { - V00, V01, V02, V03, V04, V05, V06, V07, V08, V09, - V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, - V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, - V30, V31, V32, V33, V34, V35, V36, V37, V38, V39, - } - - impl CLike for Bar { - fn to_u32(&self) -> u32 { - *self as u32 - } - - unsafe fn from_u32(v: u32) -> Bar { - mem::transmute(v) - } - } - - let mut set = EnumSet::new(); - set.insert(Bar::V32); - } -} diff --git a/tests/test.rs b/tests/test.rs new file mode 100644 index 0000000..56604fd --- /dev/null +++ b/tests/test.rs @@ -0,0 +1,244 @@ +extern crate enum_set; +#[macro_use] +extern crate enum_set_derive; + +use Foo::*; +use std::mem; + +use enum_set::{EnumSet, CLike}; + +#[derive(Copy, Clone, CLike, PartialEq, Debug)] +#[repr(u32)] +enum Foo { + A, B, C +} + +#[test] +fn test_new() { + let e: EnumSet = EnumSet::new(); + assert!(e.is_empty()); +} + +#[test] +fn test_debug() { + let mut e = EnumSet::new(); + assert_eq!("{}", format!("{:?}", e)); + e.insert(A); + assert_eq!("{A}", format!("{:?}", e)); + e.insert(C); + assert_eq!("{A, C}", format!("{:?}", e)); +} + +#[test] +fn test_len() { + let mut e = EnumSet::new(); + assert_eq!(e.len(), 0); + e.insert(A); + e.insert(B); + e.insert(C); + assert_eq!(e.len(), 3); + e.remove(&A); + assert_eq!(e.len(), 2); + e.clear(); + assert_eq!(e.len(), 0); +} + +/////////////////////////////////////////////////////////////////////////// +// intersect + +#[test] +fn test_two_empties_do_not_intersect() { + let e1: EnumSet = EnumSet::new(); + let e2: EnumSet = EnumSet::new(); + assert!(e1.is_disjoint(&e2)); +} + +#[test] +fn test_empty_does_not_intersect_with_full() { + let e1: EnumSet = EnumSet::new(); + + let mut e2: EnumSet = EnumSet::new(); + e2.insert(A); + e2.insert(B); + e2.insert(C); + + assert!(e1.is_disjoint(&e2)); +} + +#[test] +fn test_disjoint_intersects() { + let mut e1: EnumSet = EnumSet::new(); + e1.insert(A); + + let mut e2: EnumSet = EnumSet::new(); + e2.insert(B); + + assert!(e1.is_disjoint(&e2)); +} + +#[test] +fn test_overlapping_intersects() { + let mut e1: EnumSet = EnumSet::new(); + e1.insert(A); + + let mut e2: EnumSet = EnumSet::new(); + e2.insert(A); + e2.insert(B); + + assert!(!e1.is_disjoint(&e2)); +} + +/////////////////////////////////////////////////////////////////////////// +// contains and contains_elem + +#[test] +fn test_superset() { + let mut e1: EnumSet = EnumSet::new(); + e1.insert(A); + + let mut e2: EnumSet = EnumSet::new(); + e2.insert(A); + e2.insert(B); + + let mut e3: EnumSet = EnumSet::new(); + e3.insert(C); + + assert!(e1.is_subset(&e2)); + assert!(e2.is_superset(&e1)); + assert!(!e3.is_superset(&e2)); + assert!(!e2.is_superset(&e3)); +} + +#[test] +fn test_contains() { + let mut e1: EnumSet = EnumSet::new(); + e1.insert(A); + assert!(e1.contains(&A)); + assert!(!e1.contains(&B)); + assert!(!e1.contains(&C)); + + e1.insert(A); + e1.insert(B); + assert!(e1.contains(&A)); + assert!(e1.contains(&B)); + assert!(!e1.contains(&C)); +} + +/////////////////////////////////////////////////////////////////////////// +// iter + +#[test] +fn test_iterator() { + let mut e1: EnumSet = EnumSet::new(); + + let elems: Vec = e1.iter().collect(); + assert!(elems.is_empty()); + + e1.insert(A); + let elems: Vec<_> = e1.iter().collect(); + assert_eq!(vec![A], elems); + + e1.insert(C); + let elems: Vec<_> = e1.iter().collect(); + assert_eq!(vec![A,C], elems); + + e1.insert(C); + let elems: Vec<_> = e1.iter().collect(); + assert_eq!(vec![A,C], elems); + + e1.insert(B); + let elems: Vec<_> = e1.iter().collect(); + assert_eq!(vec![A,B,C], elems); +} + +#[test] +fn test_clone_iterator() { + let mut e: EnumSet = EnumSet::new(); + e.insert(A); + e.insert(B); + e.insert(C); + + let mut iter1 = e.iter(); + let first_elem = iter1.next(); + assert_eq!(Some(A), first_elem); + + let iter2 = iter1.clone(); + let elems1: Vec<_> = iter1.collect(); + assert_eq!(vec![B, C], elems1); + + let elems2: Vec<_> = iter2.collect(); + assert_eq!(vec![B, C], elems2); +} + +/////////////////////////////////////////////////////////////////////////// +// operators + +#[test] +fn test_operators() { + let mut e1: EnumSet = EnumSet::new(); + e1.insert(A); + e1.insert(C); + + let mut e2: EnumSet = EnumSet::new(); + e2.insert(B); + e2.insert(C); + + let e_union = e1 | e2; + let elems: Vec<_> = e_union.iter().collect(); + assert_eq!(vec![A,B,C], elems); + + let e_intersection = e1 & e2; + let elems: Vec<_> = e_intersection.iter().collect(); + assert_eq!(vec![C], elems); + + // Another way to express intersection + let e_intersection = e1 - (e1 - e2); + let elems: Vec<_> = e_intersection.iter().collect(); + assert_eq!(vec![C], elems); + + let e_subtract = e1 - e2; + let elems: Vec<_> = e_subtract.iter().collect(); + assert_eq!(vec![A], elems); + + // Bitwise XOR of two sets, aka symmetric difference + let e_symmetric_diff = e1 ^ e2; + let elems: Vec<_> = e_symmetric_diff.iter().collect(); + assert_eq!(vec![A,B], elems); + + // Another way to express symmetric difference + let e_symmetric_diff = (e1 - e2) | (e2 - e1); + let elems: Vec<_> = e_symmetric_diff.iter().collect(); + assert_eq!(vec![A,B], elems); + + // Yet another way to express symmetric difference + let e_symmetric_diff = (e1 | e2) - (e1 & e2); + let elems: Vec<_> = e_symmetric_diff.iter().collect(); + assert_eq!(vec![A,B], elems); +} + +#[test] +#[should_panic] +fn test_overflow() { + #[allow(dead_code)] + #[repr(u32)] + #[derive(Clone, Copy)] + enum Bar { + V00, V01, V02, V03, V04, V05, V06, V07, V08, V09, + V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, + V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, + V30, V31, V32, V33, V34, V35, V36, V37, V38, V39, + } + + impl CLike for Bar { + fn to_u32(&self) -> u32 { + *self as u32 + } + + unsafe fn from_u32(v: u32) -> Bar { + mem::transmute(v) + } + } + + let mut set = EnumSet::new(); + set.insert(Bar::V32); +}