Skip to main content

ephemeris_forge/
string_enum.rs

1/// Define an enum whose variants have fixed wire names.
2///
3/// The enum decodes and encodes as its names. It gets `as_str`, `Display` and
4/// `FromStr` with [`UnknownValue`] as the error.
5///
6/// ```
7/// ephemeris_forge::string_enum! {
8///     /// A color.
9///     pub enum Color("color") {
10///         /// Red.
11///         Red = "red",
12///         /// Green.
13///         Green = "green",
14///     }
15/// }
16///
17/// assert_eq!("red".parse::<Color>(), Ok(Color::Red));
18/// assert_eq!(Color::Green.as_str(), "green");
19/// ```
20#[macro_export]
21macro_rules! string_enum {
22    (
23        $(#[$meta:meta])*
24        $vis:vis enum $name:ident($kind:literal) {
25            $($(#[$variant_meta:meta])* $variant:ident = $text:literal,)+
26        }
27    ) => {
28        $(#[$meta])*
29        #[derive(Clone, Copy, Debug, Eq, ::facet::Facet, PartialEq)]
30        #[repr(u8)]
31        $vis enum $name {
32            $($(#[$variant_meta])* #[facet(rename = $text)] $variant,)+
33        }
34
35        impl $name {
36            /// The name used on the wire.
37            #[must_use]
38            pub const fn as_str(self) -> &'static str {
39                match self {
40                    $(Self::$variant => $text,)+
41                }
42            }
43        }
44
45        impl ::core::str::FromStr for $name {
46            type Err = $crate::UnknownValue;
47
48            fn from_str(raw: &str) -> Result<Self, Self::Err> {
49                match raw {
50                    $($text => Ok(Self::$variant),)+
51                    _ => Err($crate::UnknownValue {
52                        kind: $kind,
53                        value: raw.to_owned(),
54                    }),
55                }
56            }
57        }
58
59        impl ::core::fmt::Display for $name {
60            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
61                f.write_str(self.as_str())
62            }
63        }
64    };
65}
66
67/// A string that names none of the variants of an enum.
68#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
69#[error("unknown {kind} {value:?}")]
70pub struct UnknownValue {
71    /// What the enum describes, such as `merge style`.
72    pub kind: &'static str,
73    /// The rejected string.
74    pub value: String,
75}