enum_tag_macro/
lib.rs

1use proc_macro::TokenStream;
2use syn::{parse_macro_input, DeriveInput};
3
4#[macro_use]
5mod error;
6mod derive;
7
8/// Proc. macro to derive the `EnumTag` trait for the given Rust `enum`.
9///
10/// This proc. macro only works on Rust `enum` types and creates a C-like
11/// `enum` with the same variants as the input Rust `enum` without all the
12/// associated data.
13/// Also it derives an implementation of `EnumTag` for the Rust `enum` that
14/// makes it possible to create instances of the generated C-like `enum` as
15/// well as link to its identifier via `<RustEnum as EnumTag>::Tag`.
16///
17/// # Example
18///
19/// ```
20/// use enum_tag::EnumTag;
21///
22/// #[derive(EnumTag)]
23/// #[repr(u8)] // Rust needs this for `B = 42`
24/// enum Foo {
25///     A,
26///     B = 42,
27///     C(i32),
28///     D(i32, i64),
29///     E { a: i32 },
30///     F { a: i32, b: i64 },
31/// }
32///
33/// /// This is how we can access the generated C-like enum type and name it.
34/// type FooTag = <Foo as EnumTag>::Tag;
35///
36/// assert_eq!(FooTag::A, Foo::A.tag());
37/// assert_eq!(FooTag::B, Foo::B.tag());
38/// assert_eq!(FooTag::C, Foo::C(1).tag());
39/// assert_eq!(FooTag::D, Foo::D(2, 3).tag());
40/// assert_eq!(FooTag::E, Foo::E { a: 4 }.tag());
41/// assert_eq!(FooTag::F, Foo::F { a: 5, b: 6 }.tag());
42///
43/// assert_eq!(FooTag::B as u8, 42);
44/// ```
45///
46/// The above `#[derive(EnumTag)]` proc. macro will expand to roughly the following Rust code:
47///
48/// ```
49/// # #[repr(u8)] // Rust needs this for `B = 42`
50/// # enum Foo {
51/// #     A,
52/// #     B = 42,
53/// #     C(i32),
54/// #     D(i32, i64),
55/// #     E { a: i32 },
56/// #     F { a: i32, b: i64 },
57/// # }
58/// #
59/// const _: () = {
60///     #[derive(
61///         ::core::fmt::Debug,
62///         ::core::clone::Clone,
63///         ::core::marker::Copy,
64///         ::core::cmp::PartialEq,
65///         ::core::cmp::Eq,
66///         ::core::cmp::PartialOrd,
67///         ::core::cmp::Ord,
68///         ::core::hash::Hash,
69///     )]
70///     pub enum FooTag {
71///         A,
72///         B = 42,
73///         C,
74///         D,
75///         E,
76///         F,
77///     }
78///
79///     impl ::enum_tag::EnumTag for Foo {
80///         type Tag = FooTag;
81///
82///         fn tag(&self) -> <Self as ::enum_tag::EnumTag>::Tag {
83///             match self {
84///                 Self::A { .. } => <Self as ::enum_tag::EnumTag>::Tag::A,
85///                 Self::B { .. } => <Self as ::enum_tag::EnumTag>::Tag::B,
86///                 Self::C { .. } => <Self as ::enum_tag::EnumTag>::Tag::C,
87///                 Self::D { .. } => <Self as ::enum_tag::EnumTag>::Tag::D,
88///                 Self::E { .. } => <Self as ::enum_tag::EnumTag>::Tag::E,
89///                 Self::F { .. } => <Self as ::enum_tag::EnumTag>::Tag::F,
90///             }
91///         }
92///     }
93/// };
94/// ```
95#[proc_macro_derive(EnumTag)]
96pub fn enum_tag(input: TokenStream) -> TokenStream {
97    derive::enum_tag(parse_macro_input!(input as DeriveInput)).into()
98}