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