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}