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            // allow(unused_doc_comments): variant doc attrs are propagated into
49            // match arms so that #[cfg] attrs also propagate; the doc attrs
50            // are harmless noise here.
51            #[allow(unused_doc_comments)]
52            pub fn as_str(&self) -> &'static str {
53                match self {
54                    $(
55                        $(#[$var_meta])*
56                        $Name::$variant => $wire,
57                    )+
58                }
59            }
60        }
61
62        impl std::fmt::Display for $Name {
63            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64                f.write_str(self.as_str())
65            }
66        }
67
68        impl AsRef<str> for $Name {
69            fn as_ref(&self) -> &str {
70                self.as_str()
71            }
72        }
73
74        impl std::str::FromStr for $Name {
75            type Err = $Err;
76
77            #[allow(unused_doc_comments)]
78            fn from_str(s: &str) -> Result<Self, Self::Err> {
79                $(
80                    $(#[$var_meta])*
81                    if s.eq_ignore_ascii_case($wire) {
82                        return Ok($Name::$variant);
83                    }
84                )+
85                Err($Err(s.to_string()))
86            }
87        }
88    };
89}