1#![no_std]
23
24use core::fmt;
25
26#[macro_export]
49macro_rules! strum {
50 (
52 $(#[$enum_meta:meta])*
53 $enum_vis:vis enum $enum_name:ident {
54 $(
55 $(#[$variant_meta:meta])*
56 $variant_name:ident = $string:literal $(| $alias:literal)* $(= $discriminant:expr)?
57 ),* $(,)?
58 }
59 $($rest:tt)*
60 ) => {
61 $crate::strum! {@tail
62 {
63 [$(#[$enum_meta])*]
64 [$enum_vis]
65 $enum_name
66 [$([$(#[$variant_meta])*] $variant_name [$string $(| $alias)*] [$($discriminant)?])*]
67 [$($string)*]
68 }
69 $($rest)*
70 }
71 };
72 (@tail
74 { $metas:tt $vis:tt $enum_name:ident $variants:tt [$($string:literal)*] }
75 ) => {
76 $crate::strum! {@define $metas $vis $enum_name $variants []
77 [$crate::ParseError]
78 [$crate::ParseError({
79 const ALL: &'static [&'static ::core::primitive::str] = &[$($string),*];
80 &ALL
81 })]
82 }
83 };
84 (@tail
85 { $metas:tt $vis:tt $enum_name:ident $variants:tt [$($string:literal)*] }
86 $(#[$const_meta:meta])* $const_vis:vis const $const_name:ident;
87 ) => {
88 $crate::strum! {@define $metas $vis $enum_name $variants
89 [[$(#[$const_meta])*] $const_vis const $const_name]
90 [$crate::ParseError]
91 [$crate::ParseError({
92 const ALL: &'static [&'static ::core::primitive::str] = &[$($string),*];
93 &ALL
94 })]
95 }
96 };
97 (@tail
98 { $metas:tt $vis:tt $enum_name:ident $variants:tt [$($string:literal)*] }
99 throws $(#[$error_meta:meta])* $error_name:ident $(;)?
100 ) => {
101 $crate::strum! {@define $metas $vis $enum_name $variants []
102 [$error_name]
103 [$error_name]
104 }
105 $crate::strum! {@error [$(#[$error_meta])*] $vis $enum_name $error_name [$($string)*]}
106 };
107 (@tail
108 { $metas:tt $vis:tt $enum_name:ident $variants:tt [$($string:literal)*] }
109 $(#[$const_meta:meta])* $const_vis:vis const $const_name:ident;
110 throws $(#[$error_meta:meta])* $error_name:ident $(;)?
111 ) => {
112 $crate::strum! {@define $metas $vis $enum_name $variants
113 [[$(#[$const_meta])*] $const_vis const $const_name]
114 [$error_name]
115 [$error_name]
116 }
117 $crate::strum! {@error [$(#[$error_meta])*] $vis $enum_name $error_name [$($string)*]}
118 };
119 (@define
121 [$(#[$enum_meta:meta])*]
122 [$enum_vis:vis]
123 $enum_name:ident
124 [$(
125 [$(#[$variant_meta:meta])*]
126 $variant_name:ident
127 [$string:literal $(| $alias:literal)*]
128 [$($discriminant:expr)?]
129 )*]
130 $konst:tt
131 [$error_ty:ty]
132 [$error_new:expr]
133 ) => {
134 $(#[$enum_meta])*
135 $enum_vis enum $enum_name {
136 $(
137 $(#[$variant_meta])*
138 #[doc = ::core::concat!(" String representation: `", $string, "`")]
139 $variant_name $(= $discriminant)?,
140 )*
141 }
142 $crate::strum! {@konst $enum_name $konst [$($variant_name)*]}
143 const _: () = {
144 use ::core;
145 impl core::fmt::Display for $enum_name {
146 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
147 fn as_str(e: &$enum_name) -> &core::primitive::str {
148 match *e {
149 $($enum_name::$variant_name => $string),*
150 }
151 }
152 core::fmt::Formatter::write_str(f, as_str(self))
153 }
154 }
155 impl core::str::FromStr for $enum_name {
156 type Err = $error_ty;
157 fn from_str(s: &core::primitive::str) -> core::result::Result<Self, Self::Err> {
158 match s {
159 $(
160 $string $(| $alias )* => core::result::Result::Ok(Self::$variant_name),
161 )*
162 _ => core::result::Result::Err($error_new)
163 }
164 }
165 }
166 };
167 };
168 (@konst $enum_name:ident [] $variant_names:tt) => {};
169 (@konst $enum_name:ident [[] $vis:vis const $konst:ident] $variant_names:tt) => {
170 $crate::strum! {@konst $enum_name
171 [[#[doc = " Every variant of this enum, in declaration order."]] $vis const $konst]
172 $variant_names
173 }
174 };
175 (@konst $enum_name:ident [[$(#[$const_meta:meta])+] $vis:vis const $konst:ident] [$($variant_name:ident)*]) => {
176 impl $enum_name {
177 $(#[$const_meta])+
178 $vis const $konst: [Self; <[Self]>::len(&[$(Self::$variant_name),*])] =
179 [$(Self::$variant_name),*];
180 }
181 };
182 (@error
184 [$(#[$error_meta:meta])*]
185 [$vis:vis]
186 $enum_name:ident
187 $error_name:ident
188 [$($string:literal)*]
189 ) => {
190 $(#[$error_meta])*
191 #[doc = ::core::concat!(" Error returned when parsing [`", ::core::stringify!($enum_name), "`] from a string.")]
192 $vis struct $error_name;
193 const _: () = {
194 use ::core;
195 impl core::fmt::Display for $error_name {
196 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
197 const ALL: &'static [&'static core::primitive::str] = &[$($string),*];
198 core::fmt::Display::fmt(&$crate::ParseError(&ALL), f)
199 }
200 }
201 impl core::fmt::Debug for $error_name {
202 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
203 let mut f = core::fmt::Formatter::debug_tuple(f, core::stringify!($error_name));
204 core::fmt::DebugTuple::field(&mut f, &core::format_args!("{}", self));
205 core::fmt::DebugTuple::finish(&mut f)
206 }
207 }
208 impl core::error::Error for $error_name {}
209 };
210 };
211}
212
213#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
215pub struct ParseError(#[doc(hidden)] pub &'static &'static [&'static str]);
216
217impl fmt::Display for ParseError {
218 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219 match self.0 {
220 [] => f.write_str("Uninhabited type is impossible to parse"),
221 [first] => f.write_fmt(format_args!("Expected string `{first}`")),
222 [first, second] => f.write_fmt(format_args!("Expected `{first}` or `{second}`")),
223 [first, rest @ .., last] => {
224 f.write_fmt(format_args!("Expected one of `{first}`"))?;
225 for it in rest {
226 f.write_fmt(format_args!(", `{it}`"))?
227 }
228 f.write_fmt(format_args!(", or `{last}`"))
229 }
230 }
231 }
232}
233
234impl fmt::Debug for ParseError {
235 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236 f.debug_tuple("ParseError")
237 .field(&format_args!("{self}"))
238 .finish()
239 }
240}
241
242impl core::error::Error for ParseError {}