Skip to content

Commit 3dc6963

Browse files
committed
Support deriving CLike
Fixes #22
1 parent e3b86fb commit 3dc6963

File tree

5 files changed

+343
-268
lines changed

5 files changed

+343
-268
lines changed

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,9 @@ homepage = "https://github.com/contain-rs/enum-set"
1313
documentation = "https://contain-rs.github.io/enum-set/enum_set"
1414
keywords = ["data-structures"]
1515
readme = "README.md"
16+
17+
[dev-dependencies]
18+
enum-set-derive = { version = "0.1.0", path = "enum-set-derive" }
19+
20+
[workspace]
21+
members = ["enum-set-derive"]

enum-set-derive/Cargo.toml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[package]
2+
3+
name = "enum-set-derive"
4+
version = "0.1.0"
5+
license = "MIT/Apache-2.0"
6+
description = "Macros 1.1 implementation of #[derive(EnumMap)]"
7+
authors = [
8+
"Konrad Borowski <[email protected]>"
9+
]
10+
11+
repository = "https://github.com/contain-rs/enum-set"
12+
homepage = "https://github.com/contain-rs/enum-set"
13+
documentation = "https://contain-rs.github.io/enum-set/enum_set"
14+
keywords = ["data-structures"]
15+
readme = "README.md"
16+
17+
[lib]
18+
proc-macro = true
19+
20+
[dependencies]
21+
syn = "0.11.11"
22+
quote = "0.3.15"

enum-set-derive/src/lib.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
extern crate proc_macro;
2+
extern crate syn;
3+
#[macro_use]
4+
extern crate quote;
5+
6+
use std::iter;
7+
8+
use proc_macro::TokenStream;
9+
use syn::{Body, Ident, Variant, VariantData};
10+
use quote::Tokens;
11+
12+
fn generate_enum_code(name: &Ident, variants: &[Variant]) -> Tokens {
13+
for (count,
14+
&Variant {
15+
ref data,
16+
ref discriminant,
17+
..
18+
}) in variants.iter().enumerate()
19+
{
20+
if count == 32 {
21+
panic!("#[derive(CLike)] supports at most 32 variants");
22+
}
23+
if data != &VariantData::Unit {
24+
panic!("#[derive(CLike)] requires C style style enum");
25+
}
26+
if discriminant.is_some() {
27+
panic!("#[derive(CLike)] doesn't currently support discriminants");
28+
}
29+
}
30+
31+
let variant = variants.iter().map(|variant| &variant.ident);
32+
let counter = 0..variants.len() as u32;
33+
let names = iter::repeat(name);
34+
35+
let to_u32 = if variants.len() == 0 {
36+
quote! { unreachable!() }
37+
} else {
38+
quote! { *self as u32 }
39+
};
40+
41+
quote! {
42+
impl ::enum_set::CLike for #name {
43+
unsafe fn from_u32(value: u32) -> Self {
44+
match value {
45+
#(
46+
#counter => #names::#variant,
47+
)*
48+
_ => unreachable!()
49+
}
50+
}
51+
fn to_u32(&self) -> u32 {
52+
#to_u32
53+
}
54+
}
55+
}
56+
}
57+
58+
#[proc_macro_derive(CLike)]
59+
pub fn derive_clike(input: TokenStream) -> TokenStream {
60+
let input = syn::parse_macro_input(&input.to_string()).unwrap();
61+
match input.body {
62+
Body::Enum(ref variants) => generate_enum_code(&input.ident, variants),
63+
_ => panic!("#[derive(CLike)] is only defined for enums"),
64+
}.parse()
65+
.unwrap()
66+
}

src/lib.rs

Lines changed: 5 additions & 268 deletions
Original file line numberDiff line numberDiff line change
@@ -47,24 +47,15 @@ impl<E: CLike> hash::Hash for EnumSet<E> {
4747
/// A typical implementation can be seen below:
4848
///
4949
/// ```
50-
/// use enum_set::CLike;
51-
/// use std::mem;
50+
/// extern crate enum_set;
51+
/// #[macro_use]
52+
/// extern crate enum_set_derive;
5253
///
53-
/// #[derive(Clone, Copy)]
54-
/// #[repr(u32)]
54+
/// #[derive(Clone, Copy, CLike)]
5555
/// enum Foo {
5656
/// A, B, C
5757
/// }
58-
///
59-
/// impl CLike for Foo {
60-
/// fn to_u32(&self) -> u32 {
61-
/// *self as u32
62-
/// }
63-
///
64-
/// unsafe fn from_u32(v: u32) -> Foo {
65-
/// mem::transmute(v)
66-
/// }
67-
/// }
58+
/// # fn main() {}
6859
/// ```
6960
pub trait CLike {
7061
/// 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<E> {
268259
type IntoIter = Iter<E>;
269260
fn into_iter(self) -> Iter<E> { self.iter() }
270261
}
271-
272-
#[cfg(test)]
273-
mod tests {
274-
use self::Foo::*;
275-
use std::mem;
276-
277-
use super::{EnumSet, CLike};
278-
279-
#[derive(Copy, Clone, PartialEq, Debug)]
280-
#[repr(u32)]
281-
enum Foo {
282-
A, B, C
283-
}
284-
285-
impl CLike for Foo {
286-
fn to_u32(&self) -> u32 {
287-
*self as u32
288-
}
289-
290-
unsafe fn from_u32(v: u32) -> Foo {
291-
mem::transmute(v)
292-
}
293-
}
294-
295-
#[test]
296-
fn test_new() {
297-
let e: EnumSet<Foo> = EnumSet::new();
298-
assert!(e.is_empty());
299-
}
300-
301-
#[test]
302-
fn test_debug() {
303-
let mut e = EnumSet::new();
304-
assert_eq!("{}", format!("{:?}", e));
305-
e.insert(A);
306-
assert_eq!("{A}", format!("{:?}", e));
307-
e.insert(C);
308-
assert_eq!("{A, C}", format!("{:?}", e));
309-
}
310-
311-
#[test]
312-
fn test_len() {
313-
let mut e = EnumSet::new();
314-
assert_eq!(e.len(), 0);
315-
e.insert(A);
316-
e.insert(B);
317-
e.insert(C);
318-
assert_eq!(e.len(), 3);
319-
e.remove(&A);
320-
assert_eq!(e.len(), 2);
321-
e.clear();
322-
assert_eq!(e.len(), 0);
323-
}
324-
325-
///////////////////////////////////////////////////////////////////////////
326-
// intersect
327-
328-
#[test]
329-
fn test_two_empties_do_not_intersect() {
330-
let e1: EnumSet<Foo> = EnumSet::new();
331-
let e2: EnumSet<Foo> = EnumSet::new();
332-
assert!(e1.is_disjoint(&e2));
333-
}
334-
335-
#[test]
336-
fn test_empty_does_not_intersect_with_full() {
337-
let e1: EnumSet<Foo> = EnumSet::new();
338-
339-
let mut e2: EnumSet<Foo> = EnumSet::new();
340-
e2.insert(A);
341-
e2.insert(B);
342-
e2.insert(C);
343-
344-
assert!(e1.is_disjoint(&e2));
345-
}
346-
347-
#[test]
348-
fn test_disjoint_intersects() {
349-
let mut e1: EnumSet<Foo> = EnumSet::new();
350-
e1.insert(A);
351-
352-
let mut e2: EnumSet<Foo> = EnumSet::new();
353-
e2.insert(B);
354-
355-
assert!(e1.is_disjoint(&e2));
356-
}
357-
358-
#[test]
359-
fn test_overlapping_intersects() {
360-
let mut e1: EnumSet<Foo> = EnumSet::new();
361-
e1.insert(A);
362-
363-
let mut e2: EnumSet<Foo> = EnumSet::new();
364-
e2.insert(A);
365-
e2.insert(B);
366-
367-
assert!(!e1.is_disjoint(&e2));
368-
}
369-
370-
///////////////////////////////////////////////////////////////////////////
371-
// contains and contains_elem
372-
373-
#[test]
374-
fn test_superset() {
375-
let mut e1: EnumSet<Foo> = EnumSet::new();
376-
e1.insert(A);
377-
378-
let mut e2: EnumSet<Foo> = EnumSet::new();
379-
e2.insert(A);
380-
e2.insert(B);
381-
382-
let mut e3: EnumSet<Foo> = EnumSet::new();
383-
e3.insert(C);
384-
385-
assert!(e1.is_subset(&e2));
386-
assert!(e2.is_superset(&e1));
387-
assert!(!e3.is_superset(&e2));
388-
assert!(!e2.is_superset(&e3));
389-
}
390-
391-
#[test]
392-
fn test_contains() {
393-
let mut e1: EnumSet<Foo> = EnumSet::new();
394-
e1.insert(A);
395-
assert!(e1.contains(&A));
396-
assert!(!e1.contains(&B));
397-
assert!(!e1.contains(&C));
398-
399-
e1.insert(A);
400-
e1.insert(B);
401-
assert!(e1.contains(&A));
402-
assert!(e1.contains(&B));
403-
assert!(!e1.contains(&C));
404-
}
405-
406-
///////////////////////////////////////////////////////////////////////////
407-
// iter
408-
409-
#[test]
410-
fn test_iterator() {
411-
let mut e1: EnumSet<Foo> = EnumSet::new();
412-
413-
let elems: Vec<Foo> = e1.iter().collect();
414-
assert!(elems.is_empty());
415-
416-
e1.insert(A);
417-
let elems: Vec<_> = e1.iter().collect();
418-
assert_eq!(vec![A], elems);
419-
420-
e1.insert(C);
421-
let elems: Vec<_> = e1.iter().collect();
422-
assert_eq!(vec![A,C], elems);
423-
424-
e1.insert(C);
425-
let elems: Vec<_> = e1.iter().collect();
426-
assert_eq!(vec![A,C], elems);
427-
428-
e1.insert(B);
429-
let elems: Vec<_> = e1.iter().collect();
430-
assert_eq!(vec![A,B,C], elems);
431-
}
432-
433-
#[test]
434-
fn test_clone_iterator() {
435-
let mut e: EnumSet<Foo> = EnumSet::new();
436-
e.insert(A);
437-
e.insert(B);
438-
e.insert(C);
439-
440-
let mut iter1 = e.iter();
441-
let first_elem = iter1.next();
442-
assert_eq!(Some(A), first_elem);
443-
444-
let iter2 = iter1.clone();
445-
let elems1: Vec<_> = iter1.collect();
446-
assert_eq!(vec![B, C], elems1);
447-
448-
let elems2: Vec<_> = iter2.collect();
449-
assert_eq!(vec![B, C], elems2);
450-
}
451-
452-
///////////////////////////////////////////////////////////////////////////
453-
// operators
454-
455-
#[test]
456-
fn test_operators() {
457-
let mut e1: EnumSet<Foo> = EnumSet::new();
458-
e1.insert(A);
459-
e1.insert(C);
460-
461-
let mut e2: EnumSet<Foo> = EnumSet::new();
462-
e2.insert(B);
463-
e2.insert(C);
464-
465-
let e_union = e1 | e2;
466-
let elems: Vec<_> = e_union.iter().collect();
467-
assert_eq!(vec![A,B,C], elems);
468-
469-
let e_intersection = e1 & e2;
470-
let elems: Vec<_> = e_intersection.iter().collect();
471-
assert_eq!(vec![C], elems);
472-
473-
// Another way to express intersection
474-
let e_intersection = e1 - (e1 - e2);
475-
let elems: Vec<_> = e_intersection.iter().collect();
476-
assert_eq!(vec![C], elems);
477-
478-
let e_subtract = e1 - e2;
479-
let elems: Vec<_> = e_subtract.iter().collect();
480-
assert_eq!(vec![A], elems);
481-
482-
// Bitwise XOR of two sets, aka symmetric difference
483-
let e_symmetric_diff = e1 ^ e2;
484-
let elems: Vec<_> = e_symmetric_diff.iter().collect();
485-
assert_eq!(vec![A,B], elems);
486-
487-
// Another way to express symmetric difference
488-
let e_symmetric_diff = (e1 - e2) | (e2 - e1);
489-
let elems: Vec<_> = e_symmetric_diff.iter().collect();
490-
assert_eq!(vec![A,B], elems);
491-
492-
// Yet another way to express symmetric difference
493-
let e_symmetric_diff = (e1 | e2) - (e1 & e2);
494-
let elems: Vec<_> = e_symmetric_diff.iter().collect();
495-
assert_eq!(vec![A,B], elems);
496-
}
497-
498-
#[test]
499-
#[should_panic]
500-
fn test_overflow() {
501-
#[allow(dead_code)]
502-
#[repr(u32)]
503-
#[derive(Clone, Copy)]
504-
enum Bar {
505-
V00, V01, V02, V03, V04, V05, V06, V07, V08, V09,
506-
V10, V11, V12, V13, V14, V15, V16, V17, V18, V19,
507-
V20, V21, V22, V23, V24, V25, V26, V27, V28, V29,
508-
V30, V31, V32, V33, V34, V35, V36, V37, V38, V39,
509-
}
510-
511-
impl CLike for Bar {
512-
fn to_u32(&self) -> u32 {
513-
*self as u32
514-
}
515-
516-
unsafe fn from_u32(v: u32) -> Bar {
517-
mem::transmute(v)
518-
}
519-
}
520-
521-
let mut set = EnumSet::new();
522-
set.insert(Bar::V32);
523-
}
524-
}

0 commit comments

Comments
 (0)