Skip to main content

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 gen_option;
14mod symbol;
15mod template;
16
17use proc_macro::TokenStream;
18use syn::{DeriveInput, parse_macro_input};
19
20/// Generate string conversion for an enum.
21///
22/// Supports unit enums by default. Use `#[enum_str(default)]` for non-unit enums.
23///
24/// This macro generates inherent items and impls on the enum:
25///
26/// - `const fn as_name(&self) -> &'static str`
27/// - `const fn as_aliases(&self) -> &'static [&'static str]`
28/// - `const ALL_NAMES: [&'static str; N]`
29/// - `const ALL_ALIASES: [&'static str; M]`
30/// - `impl From<T> for &'static str`
31/// - `impl AsRef<str> for T`
32/// - `impl Display for T`
33/// - `impl FromStr for T`
34/// - `impl TryFrom<&str> for T`
35/// - Error struct for invalid conversion from str
36///
37/// # Container attributes
38///
39/// - `#[enum_str(rename_all = "snake_case")]`: rename all variants by rule
40/// - `#[enum_str(alias_all = "lowercase")]`: add aliases to all variants by rule (repeatable)
41/// - `#[enum_str(default)]`: fills non-unit variant's fields with default value during parsing
42/// - `#[enum_str(no_rendering)]`: skip `as_name`, `as_aliases`, `ALL_NAMES`, `ALL_ALIASES`, `From`, `AsRef`, `Display`
43/// - `#[enum_str(no_parsing)]`: skip `FromStr`, `TryFrom`, and the error struct
44/// - `#[enum_str(as_name(name = ..., vis = "...", enable/disable))]`: control the `as_name` method
45/// - `#[enum_str(as_aliases(...))]`: control the `as_aliases` method
46/// - `#[enum_str(all_names(...))]`: control the `ALL_NAMES` constant
47/// - `#[enum_str(all_aliases(...))]`: control the `ALL_ALIASES` constant
48/// - `#[enum_str(impl_into_static_str(enable/disable))]`: control `impl From<T> for &'static str`
49/// - `#[enum_str(impl_as_ref_str(enable/disable))]`: control `impl AsRef<str> for T`
50/// - `#[enum_str(impl_display(enable/disable))]`: control `impl std::fmt::Display for T`
51/// - `#[enum_str(impl_from_str(enable/disable))]`: control `impl FromStr for T`
52/// - `#[enum_str(impl_try_from_str(enable/disable))]`: control `impl TryFrom<&str> for T`
53/// - `#[enum_str(error(name = ..., vis = "...", enable/disable))]`: control the error struct (default name `Invalid{Enum}`)
54/// - `#[enum_str(error_msg = "...")]`: custom error message template
55///
56/// # Variant attributes
57///
58/// - `#[enum_str(rename = "custom_name")]`: override the variant's name
59/// - `#[enum_str(alias = "alt")]`: add an alias (repeatable)
60/// - `#[enum_str(skip)]`: skip variant from parsing and `ALL_NAMES` / `ALL_ALIASES`
61///
62/// # Available rename rules
63///
64/// - `lowercase`
65/// - `UPPERCASE`
66/// - `PascalCase`
67/// - `camelCase`
68/// - `snake_case`
69/// - `SCREAMING_SNAKE_CASE`
70/// - `kebab-case`
71/// - `SCREAMING-KEBAB-CASE`
72///
73/// # Available error message template variables
74///
75/// - `{name}`: the enum's type name
76/// - `{input}`: the invalid input string (requires allocation)
77/// - `{names}`: all variant primary names
78/// - `{aliases}`: all names and aliases
79///
80/// List variables accept format modifiers:
81///
82/// - `{names}`: default, comma-separated, double-quoted: `"Bar", "Baz"`
83/// - `{names:|}`: custom separator, no quotes: `Bar|Baz`
84/// - `{names: - :'}`: custom separator and quote char: `'Bar' - 'Baz'`
85///
86/// Use `{{` and `}}` for literal braces. `:` cannot be used as a separator or quote character.
87///
88/// # Example
89///
90/// ```
91/// use enum_helper::EnumStr;
92///
93/// #[derive(EnumStr, Debug, PartialEq, Eq)]
94/// #[enum_str(rename_all = "lowercase")]
95/// pub enum Foo {
96///     Bar,
97///     #[enum_str(alias = "bazzz")]
98///     Baz,
99/// }
100///
101/// assert_eq!(Foo::Bar.as_name(), "bar");
102/// assert_eq!("bazzz".parse::<Foo>().unwrap(), Foo::Baz);
103/// ```
104///
105/// [`EnumStr`]: https://docs.rs/enum-helper/latest/enum_helper/derive.EnumStr.html
106#[proc_macro_derive(EnumStr, attributes(enum_str))]
107pub fn derive_enum_str(input: TokenStream) -> TokenStream {
108    let input = parse_macro_input!(input as DeriveInput);
109
110    let ir = match enum_str::parse::parse_ir(&input) {
111        Ok(ir) => ir,
112        Err(err) => return err.into_compile_error().into(),
113    };
114
115    enum_str::generate::generate(ir).into()
116}
117
118/// Generate an array of all variants and a variant count.
119///
120/// This macro generates inherent constants on the enum:
121///
122/// - `const ALL: [Self; N]`
123/// - `const COUNT: usize`
124///
125/// `ALL` requires unit enums (it builds an array of variant values). For
126/// non-unit enums, use `#[enum_all(all(disable))]` to derive only `COUNT`, or
127/// `#[enum_all(skip)]` on the non-unit variants.
128///
129/// # Container attributes
130///
131/// - `#[enum_all(all(name = ..., vis = "...", enable/disable))]`: control the `ALL` constant
132/// - `#[enum_all(count(name = ..., vis = "...", enable/disable))]`: control the `COUNT` constant
133///
134/// # Variant attributes
135///
136/// - `#[enum_all(skip)]`: exclude this variant from `ALL` and `COUNT`
137///
138/// # Example
139///
140/// ```
141/// use enum_helper::EnumAll;
142///
143/// #[derive(EnumAll, Debug, PartialEq, Eq)]
144/// enum Foo {
145///     Bar,
146///     Baz,
147///     #[enum_all(skip)]
148///     Skipped
149/// }
150///
151/// assert_eq!(Foo::ALL, [Foo::Bar, Foo::Baz]);
152/// assert_eq!(Foo::COUNT, 2);
153/// ```
154///
155/// [`EnumAll`]: https://docs.rs/enum-helper/latest/enum_helper/derive.EnumAll.html
156#[proc_macro_derive(EnumAll, attributes(enum_all))]
157pub fn derive_enum_all(input: TokenStream) -> TokenStream {
158    let input = parse_macro_input!(input as DeriveInput);
159
160    let ir = match enum_all::parse::parse_ir(&input) {
161        Ok(ir) => ir,
162        Err(err) => return err.into_compile_error().into(),
163    };
164
165    enum_all::generate::generate(ir).into()
166}
167
168/// Generate a unit kind enum from a data-carrying enum.
169///
170/// This macro generates:
171///
172/// - Kind enum (default name `{Enum}Kind`)
173/// - `const fn kind(&self) -> {Enum}Kind` inherent method
174///
175/// # Container attributes
176///
177/// - `#[enum_kind(name = MyKind)]`: custom name for the generated kind enum (default: `{Enum}Kind`)
178/// - `#[enum_kind(attr(...))]`: forward attributes to the generated kind enum (repeatable)
179/// - `#[enum_kind(no_default_derive)]`: disable the default `Debug, Clone, Copy, PartialEq, Eq` derives on generated kind enum
180///
181/// # Variant attributes
182///
183/// - `#[enum_kind(rename = Bar)]`: rename the kind variant
184/// - `#[enum_kind(attr(...))]`: forward attributes to the kind variant (repeatable)
185///
186/// # Example
187///
188/// ```
189/// use enum_helper::EnumKind;
190///
191/// #[derive(EnumKind)]
192/// enum Message {
193///     Text(String),
194///     Quit,
195/// }
196///
197/// let msg = Message::Text("hello".into());
198/// assert_eq!(msg.kind(), MessageKind::Text);
199/// ```
200///
201/// [`EnumKind`]: https://docs.rs/enum-helper/latest/enum_helper/derive.EnumKind.html
202#[proc_macro_derive(EnumKind, attributes(enum_kind))]
203pub fn derive_enum_kind(input: TokenStream) -> TokenStream {
204    let input = parse_macro_input!(input as DeriveInput);
205
206    let ir = match enum_kind::parse::parse_ir(&input) {
207        Ok(ir) => ir,
208        Err(err) => return err.into_compile_error().into(),
209    };
210
211    enum_kind::generate::generate(ir).into()
212}