Expand description
Derive core::str::FromStr and core::fmt::Display for enums whose
variants serialize to hierarchical, delimiter-separated paths.
use enum_path::EnumPath;
#[derive(EnumPath, Clone, Debug, PartialEq, Eq)]
#[enum_path(FromStr, Display, rename_all = "snake_case")]
enum Action
{
Exit,
SendMessage(String),
SetState(State),
}
#[derive(EnumPath, Clone, Debug, PartialEq, Eq)]
#[enum_path(FromStr, Display)]
enum State
{
Idle,
Ready,
}
let parsed: Action = "set_state.Idle".parse().unwrap();
assert_eq!(parsed.to_string(), "set_state.Idle");§Enum-level attributes
Attached as #[enum_path(...)] on the enum definition. Multiple flags
may appear in the same attribute, comma-separated.
| Attribute | Meaning |
|---|---|
FromStr | Derive core::str::FromStr for the enum. |
Display | Derive core::fmt::Display for the enum. |
rename_all = "..." | Apply a case convention to every variant; see Rename casing. |
delimiter = "..." | Separator between a variant name and its inner type; defaults to ".". |
case_insensitive | Match variant names with ASCII case-insensitive comparison. |
error = MyError | Use a custom error type from generated FromStr impls; see Custom error types. |
crate = path | Override the runtime crate path when enum_path is re-exported. |
§Per-variant attributes
| Attribute | Meaning |
|---|---|
rename = "..." | Override the serialized name for this variant. |
§Variant shape requirements
Every variant must be either a unit variant (e.g. Exit) or a single-field
tuple variant (e.g. SendMessage(String)). Multi-field tuple variants
and named-field variants are rejected at compile time.
The inner type of a single-field tuple variant must implement
core::fmt::Display when the enum derives Display, and
core::str::FromStr when the enum derives FromStr. There is no
Self::Item: FromStr bound on the generated impl; instead the macro
generates a <T as FromStr>::from_str(rest) call inline, which
produces a type error at the call site if the bound is not satisfied.
§Custom error types
Pass #[enum_path(FromStr, error = MyError)] to override the error
type used by the generated FromStr impl. The supplied type must
implement core::convert::From<enum_path::Error> so the macro can
construct it from a parse failure:
use enum_path::EnumPath;
#[derive(Debug)]
struct MyError(enum_path::Error);
impl From<enum_path::Error> for MyError
{
fn from(e: enum_path::Error) -> Self
{
Self(e)
}
}
#[derive(EnumPath, Clone, Debug, PartialEq, Eq)]
#[enum_path(FromStr, error = MyError)]
enum Thing
{
Foo,
}The generated FromStr::Err type becomes MyError; the bound is
MyError: From<enum_path::Error>. The struct field
Error::expected always holds the original enum’s name regardless
of any wrapping conversion.
§Rename casing
rename_all accepts the usual serde-style values, but the algorithm
is built around ASCII case detection and may diverge from serde for
some inputs:
"lowercase"/"UPPERCASE"callstr::to_lowercase/to_uppercaseon the entire identifier with no word splitting."snake_case","SCREAMING_SNAKE_CASE","kebab-case","SCREAMING-KEBAB-CASE","PascalCase","camelCase"first split the identifier into words by walking ASCII case boundaries and digits, then join the lowercased words with the appropriate separator.
Caveats of the word splitter:
- Word boundaries are detected per-character via
char::is_lowercase,char::is_ascii_digit, andchar::is_alphanumeric. Identifiers containing non-ASCII letters are split on uppercase / lowercase transitions of those letters (soƑōőƂɑρsplits asƒōő+ƃɑρ). - The splitter treats any non-alphanumeric ASCII character as a word
boundary and drops it (
Foo_Bar->["Foo", "Bar"]). - A digit followed by a non-digit alphabetic character starts a new
word (
foo23bar->foo23.bar), and a non-digit character followed by a digit does not (soFoo2->["Foo2"]). - Unicode width casing (e.g. the ligature
ffl) goes throughchar::to_lowercase/to_uppercase, which can produce multiple output characters from one input character.
If you need behavior the rules above don’t capture, set the variant
name explicitly with #[enum_path(rename = "...")].
§Uniqueness
The macro rejects (at compile time) two variants that resolve to the
same serialized name, since FromStr would otherwise become
order-dependent. It also rejects a serialized name that is a prefix
of another serialized name followed by the configured delimiter
(e.g. variants named foo and foo.bar would alias under the
default . delimiter).
Structs§
- Error
- Error returned by generated
FromStrimplementations.