Skip to main content

sip_header/
macros.rs

1/// Generates a non-exhaustive enum mapping Rust variants to canonical protocol strings.
2///
3/// Produces: enum definition + `as_str()` + `Display` + `AsRef<str>` + `FromStr`.
4/// `FromStr` uses `eq_ignore_ascii_case` — appropriate for user-facing catalog
5/// types (header names, variable names) where input may come from config files.
6/// Wire protocol state types use hand-written strict `FromStr` instead.
7/// The error type must be defined separately (matching existing crate patterns like
8/// `ParseEventHeaderError`, `ParseChannelVariableError`).
9///
10/// # Example
11///
12/// ```ignore
13/// define_header_enum! {
14///     error_type: ParseMyEnumError,
15///     /// Doc comment for the enum.
16///     pub enum MyEnum {
17///         Foo => "foo-wire",
18///         Bar => "bar-wire",
19///     }
20/// }
21/// ```
22#[macro_export]
23macro_rules! define_header_enum {
24    (
25        error_type: $Err:ident,
26        $(#[$enum_meta:meta])*
27        $vis:vis enum $Name:ident {
28            $(
29                $(#[$var_meta:meta])*
30                $variant:ident => $wire:literal
31            ),+ $(,)?
32        }
33    ) => {
34        $(#[$enum_meta])*
35        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
36        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
37        #[non_exhaustive]
38        #[allow(missing_docs)]
39        $vis enum $Name {
40            $(
41                $(#[$var_meta])*
42                $variant,
43            )+
44        }
45
46        impl $Name {
47            /// Canonical protocol string.
48            pub fn as_str(&self) -> &'static str {
49                match self {
50                    $( $Name::$variant => $wire, )+
51                }
52            }
53        }
54
55        impl std::fmt::Display for $Name {
56            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57                f.write_str(self.as_str())
58            }
59        }
60
61        impl AsRef<str> for $Name {
62            fn as_ref(&self) -> &str {
63                self.as_str()
64            }
65        }
66
67        impl std::str::FromStr for $Name {
68            type Err = $Err;
69
70            fn from_str(s: &str) -> Result<Self, Self::Err> {
71                $(
72                    if s.eq_ignore_ascii_case($wire) {
73                        return Ok($Name::$variant);
74                    }
75                )+
76                Err($Err(s.to_string()))
77            }
78        }
79    };
80}