enum_helper_derive/lib.rs
1//! Procedural macro implementations for [`enum-helper`].
2//!
3//! You normally depend on `enum-helper` (which re-exports these macros)
4//! rather than using this crate directly.
5//!
6//! [`enum-helper`]: https://docs.rs/enum-helper
7
8mod attr;
9mod ctxt;
10mod enum_all;
11mod enum_kind;
12mod enum_str;
13mod symbol;
14mod template;
15
16use proc_macro::TokenStream;
17use syn::{DeriveInput, parse_macro_input};
18
19/// Derive the [`EnumStr`] trait to convert between a unit enum and string.
20///
21/// `EnumStr` only supports unit enums (enums where all variants have no
22/// fields).
23///
24/// This macro generates:
25///
26/// - `impl EnumStr for T`
27/// - `impl From<T> for &'static str`
28/// - `impl AsRef<str> for T`
29/// - `impl FromStr for T`
30/// - `impl TryFrom<&str> for T`
31/// - Error struct for invalid conversion from str
32///
33/// # Container attributes
34///
35/// - `#[enum_str(rename_all = "snake_case")]`: rename all variants by rule
36/// - `#[enum_str(alias_all = "lowercase")]`: add aliases to all variants by rule (repeatable)
37/// - `#[enum_str(error_name = InvalidFoo)]`: custom error type name (default: `Invalid{Enum}`)
38/// - `#[enum_str(error_msg = "…")]`: custom error message template
39/// - `#[enum_str(no_rendering)]`: skip `EnumStr`, `From`, `AsRef` impls
40/// - `#[enum_str(no_parsing)]`: skip `FromStr`, `TryFrom`, and the error struct
41/// - `#[enum_str(no_error_struct)]`: skip the generated error struct (bring your own)
42///
43/// # Variant attributes
44///
45/// - `#[enum_str(rename = "custom_name")]`: override the variant's name
46/// - `#[enum_str(alias = "alt")]`: add an alias (repeatable)
47///
48/// # Available rename rules
49///
50/// - `lowercase`
51/// - `UPPERCASE`
52/// - `PascalCase`
53/// - `camelCase`
54/// - `snake_case`
55/// - `SCREAMING_SNAKE_CASE`
56/// - `kebab-case`
57/// - `SCREAMING-KEBAB-CASE`
58///
59/// # Available error message template variables
60///
61/// - `{name}`: the enum's type name
62/// - `{input}`: the invalid input string (requires allocation)
63/// - `{names}`: all variant primary names
64/// - `{aliases}`: all names and aliases
65///
66/// List variables accept format modifiers:
67///
68/// - `{names}`: default, comma-separated, double-quoted: `"Bar", "Baz"`
69/// - `{names:|}`: custom separator, no quotes: `Bar|Baz`
70/// - `{names: - :'}`: custom separator and quote char: `'Bar' - 'Baz'`
71///
72/// Use `{{` and `}}` for literal braces. `:` cannot be used as a separator or quote character.
73///
74/// # Example
75///
76/// ```
77/// use enum_helper::EnumStr;
78///
79/// #[derive(EnumStr, Debug, PartialEq, Eq)]
80/// #[enum_str(rename_all = "lowercase")]
81/// pub enum Foo {
82/// Bar,
83/// #[enum_str(alias = "bazzz")]
84/// Baz,
85/// }
86///
87/// assert_eq!(Foo::Bar.as_name(), "bar");
88/// assert_eq!("bazzz".parse::<Foo>().unwrap(), Foo::Baz);
89/// ```
90///
91/// [`EnumStr`]: https://docs.rs/enum-helper/latest/enum_helper/trait.EnumStr.html
92#[proc_macro_derive(EnumStr, attributes(enum_str))]
93pub fn derive_enum_str(input: TokenStream) -> TokenStream {
94 let input = parse_macro_input!(input as DeriveInput);
95
96 let ir = match enum_str::parse::parse_ir(&input) {
97 Ok(ir) => ir,
98 Err(err) => return err.into_compile_error().into(),
99 };
100
101 enum_str::generate::generate(ir).into()
102}
103
104/// Derive the [`EnumAll`] trait and generate an array of all variants.
105///
106/// This macro generates:
107///
108/// - `impl EnumAll for T`
109///
110/// # Container attributes
111///
112/// *(none)*
113///
114/// # Variant attributes
115///
116/// - `#[enum_all(skip)]`: exclude this variant from the `ALL` array
117///
118/// # Example
119///
120/// ```
121/// use enum_helper::EnumAll;
122///
123/// #[derive(EnumAll, Debug, PartialEq, Eq)]
124/// enum Foo {
125/// Bar,
126/// Baz,
127/// #[enum_all(skip)]
128/// Skipped
129/// }
130///
131/// assert_eq!(Foo::ALL, [Foo::Bar, Foo::Baz]);
132/// ```
133///
134/// [`EnumAll`]: https://docs.rs/enum-helper/latest/enum_helper/trait.EnumAll.html
135#[proc_macro_derive(EnumAll, attributes(enum_all))]
136pub fn derive_enum_all(input: TokenStream) -> TokenStream {
137 let input = parse_macro_input!(input as DeriveInput);
138
139 let ir = match enum_all::parse::parse_ir(&input) {
140 Ok(ir) => ir,
141 Err(err) => return err.into_compile_error().into(),
142 };
143
144 enum_all::generate::generate(ir).into()
145}
146
147/// Derive the [`EnumKind`] trait and generate a unit kind enum from a data-carrying enum.
148///
149/// This macro generates:
150///
151/// - Kind enum
152/// - `impl EnumKind for T`
153///
154/// # Container attributes
155///
156/// - `#[enum_kind(name = MyKind)]`: custom name for the generated kind enum (default: `{Enum}Kind`)
157/// - `#[enum_kind(attr(...))]`: forward attributes to the generated kind enum (repeatable)
158/// - `#[enum_kind(no_default_derive)]`: disable the default `Debug, Clone, Copy, PartialEq, Eq` derives on generated kind enum
159///
160/// # Variant attributes
161///
162/// - `#[enum_kind(rename = Bar)]`: rename the kind variant
163/// - `#[enum_kind(attr(...))]`: forward attributes to the kind variant (repeatable)
164///
165/// # Example
166///
167/// ```
168/// use enum_helper::EnumKind;
169///
170/// #[derive(EnumKind)]
171/// enum Message {
172/// Text(String),
173/// Quit,
174/// }
175///
176/// let msg = Message::Text("hello".into());
177/// assert_eq!(msg.kind(), MessageKind::Text);
178/// ```
179///
180/// [`EnumKind`]: https://docs.rs/enum-helper/latest/enum_helper/trait.EnumKind.html
181#[proc_macro_derive(EnumKind, attributes(enum_kind))]
182pub fn derive_enum_kind(input: TokenStream) -> TokenStream {
183 let input = parse_macro_input!(input as DeriveInput);
184
185 let ir = match enum_kind::parse::parse_ir(&input) {
186 Ok(ir) => ir,
187 Err(err) => return err.into_compile_error().into(),
188 };
189
190 enum_kind::generate::generate(ir).into()
191}