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}