enum_map_derive/
lib.rs

1// SPDX-FileCopyrightText: 2017 - 2022 Kamila Borowska <kamila@borowska.pw>
2// SPDX-FileCopyrightText: 2018 hcpl <hcpl.prog@gmail.com>
3// SPDX-FileCopyrightText: 2019 mara <vmedea@protonmail.com>
4// SPDX-FileCopyrightText: 2021 Bruno Corrêa Zimmermann <brunoczim@gmail.com>
5// SPDX-FileCopyrightText: 2021 Dietrich <dietrich@teilgedanken.de>
6//
7// SPDX-License-Identifier: MIT OR Apache-2.0
8
9//! Procedural macro implementing `#[derive(Enum)]`
10//!
11//! This is supposed to used with `enum-map` crate, which provides the
12//! actual usage documentation.
13
14mod derive_enum;
15mod derive_struct;
16
17use proc_macro2::TokenStream;
18use quote::quote;
19use syn::{Data, DeriveInput, Type};
20
21/// Derive macro generating an implementation of trait `Enum`.
22///
23/// When using a derive, enum maps are maintained in the order in which
24/// enum variants are declared. This is reflected in the value returned
25/// by `Enum::into_usize`, iterators of enum map as well as
26/// `EnumMap::as_slice` method.
27///
28/// # Examples
29///
30/// ## Enums Without Payload
31/// ```
32/// use enum_map::Enum;
33///
34/// #[derive(Enum, Debug, PartialEq, Eq)]
35/// enum A {
36///     B,
37///     C,
38///     D,
39/// }
40///
41/// assert_eq!(A::B.into_usize(), 0);
42/// assert_eq!(A::C.into_usize(), 1);
43/// assert_eq!(A::D.into_usize(), 2);
44///
45/// assert_eq!(A::from_usize(0), A::B);
46/// assert_eq!(A::from_usize(1), A::C);
47/// assert_eq!(A::from_usize(2), A::D);
48/// ```
49///
50/// ## Enums With Payload
51///
52/// ```
53/// use enum_map::Enum;
54///
55/// #[derive(Enum, Debug, PartialEq, Eq)]
56/// enum A {
57///     B,
58///     C,
59///     D,
60/// }
61///
62/// #[derive(Enum, Debug, PartialEq, Eq)]
63/// enum X {
64///     Y,
65///     Z,
66/// }
67///
68/// #[derive(Enum, Debug, PartialEq, Eq)]
69/// enum Foo {
70///     Bar(bool, A),
71///     Empty,
72///     Baz { fa: A, fx: X },
73/// }
74///
75/// assert_eq!(Foo::Bar(false, A::B).into_usize(), 0);
76/// assert_eq!(Foo::Bar(false, A::D).into_usize(), 4);
77/// assert_eq!(Foo::Bar(true, A::B).into_usize(), 1);
78/// assert_eq!(Foo::Bar(true, A::C).into_usize(), 3);
79/// assert_eq!(Foo::Empty.into_usize(), 6);
80/// assert_eq!(Foo::Baz { fa: A::B, fx: X::Y }.into_usize(), 7);
81/// assert_eq!(Foo::Baz { fa: A::B, fx: X::Z }.into_usize(), 10);
82/// assert_eq!(Foo::Baz { fa: A::D, fx: X::Y }.into_usize(), 9);
83///
84/// assert_eq!(Foo::from_usize(0), Foo::Bar(false, A::B));
85/// assert_eq!(Foo::from_usize(4), Foo::Bar(false, A::D));
86/// assert_eq!(Foo::from_usize(1), Foo::Bar(true, A::B));
87/// assert_eq!(Foo::from_usize(3), Foo::Bar(true, A::C));
88/// assert_eq!(Foo::from_usize(6), Foo::Empty);
89/// assert_eq!(Foo::from_usize(7), Foo::Baz { fa: A::B, fx: X::Y });
90/// assert_eq!(Foo::from_usize(10), Foo::Baz { fa: A::B, fx: X::Z });
91/// assert_eq!(Foo::from_usize(9), Foo::Baz { fa: A::D, fx: X::Y });
92///
93/// ```
94///
95/// ## Structs
96///
97/// ```
98/// use enum_map::Enum;
99///
100/// #[derive(Enum, Debug, PartialEq, Eq)]
101/// enum A {
102///     B,
103///     C,
104///     D,
105/// }
106///
107/// #[derive(Enum, Debug, PartialEq, Eq)]
108/// enum X {
109///     Y,
110///     Z,
111/// }
112///
113/// #[derive(Enum, Debug, PartialEq, Eq)]
114/// struct Foo {
115///     bar: bool,
116///     baz: A,
117///     end: X,
118/// }
119///
120/// assert_eq!(Foo { bar: false, baz: A::B, end: X::Y }.into_usize(), 0);
121/// assert_eq!(Foo { bar: true, baz: A::B, end: X::Y }.into_usize(), 1);
122/// assert_eq!(Foo { bar: false, baz: A::D, end: X::Y }.into_usize(), 4);
123/// assert_eq!(Foo { bar: true, baz: A::C, end: X::Z }.into_usize(), 9);
124///
125/// assert_eq!(Foo::from_usize(0), Foo { bar: false, baz: A::B, end: X::Y });
126/// assert_eq!(Foo::from_usize(1), Foo { bar: true, baz: A::B, end: X::Y });
127/// assert_eq!(Foo::from_usize(4), Foo { bar: false, baz: A::D, end: X::Y });
128/// assert_eq!(Foo::from_usize(9), Foo { bar: true, baz: A::C, end: X::Z });
129/// ```
130///
131/// ## Tuple Structs
132///
133/// ```
134/// use enum_map::Enum;
135///
136/// #[derive(Enum, Debug, PartialEq, Eq)]
137/// enum A {
138///     B,
139///     C,
140///     D,
141/// }
142///
143/// #[derive(Enum, Debug, PartialEq, Eq)]
144/// enum X {
145///     Y,
146///     Z,
147/// }
148///
149/// #[derive(Enum, Debug, PartialEq, Eq)]
150/// struct Foo(bool, A, X);
151///
152/// assert_eq!(Foo(false, A::B, X::Y ).into_usize(), 0);
153/// assert_eq!(Foo(true, A::B, X::Y ).into_usize(), 1);
154/// assert_eq!(Foo(false, A::D, X::Y ).into_usize(), 4);
155/// assert_eq!(Foo(true, A::C, X::Z ).into_usize(), 9);
156///
157/// assert_eq!(Foo::from_usize(0), Foo(false, A::B, X::Y));
158/// assert_eq!(Foo::from_usize(1), Foo(true, A::B, X::Y));
159/// assert_eq!(Foo::from_usize(4), Foo(false, A::D, X::Y));
160/// assert_eq!(Foo::from_usize(9), Foo(true, A::C, X::Z));
161#[proc_macro_derive(Enum)]
162pub fn derive_enum_map(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
163    let input: DeriveInput = syn::parse(input).unwrap();
164
165    let result = match input.data {
166        Data::Enum(data_enum) => derive_enum::generate(input.ident, data_enum),
167        Data::Struct(data_struct) => derive_struct::generate(input.ident, data_struct),
168        _ => quote! { compile_error! {"#[derive(Enum)] is only defined for enums and structs"} },
169    };
170
171    result.into()
172}
173
174fn type_length(ty: &Type) -> TokenStream {
175    quote! {
176        <#ty as ::enum_map::Enum>::LENGTH
177    }
178}