Skip to main content

str_enum/
lib.rs

1#![forbid(unsafe_code)]
2//! Macro for creating an enum where all variants have an associated constant string.
3//! Syntax:
4//! ```
5//! str_enum::str_enum! {
6//!     #[phf] // Adds a PHF map
7//!     #[error_type(MyErrorType)] // Add this to opt-in to a FromStr implementation
8//!     #[derive(Clone, Copy)] // You can add derives (exceptions: de/serialize enable the `serde` feature for that, Hash which is implemented automatically to be compatible with &str since the type is Borrow<str>)
9//!     #[repr(u8)]
10//!     pub enum MyEnum {
11//!         Variant0 => "Value0"("other valid forms such as", "value0", "can go in brackets"), // note these other valid forms are only used in the enum's try_from_str method and FromStr implementation.
12//!         Variant1 = 3 => "Value1" // you can add a discriminant
13//!     }
14//! }
15//!
16//! let var0 = MyEnum::try_from_str("value0").unwrap();
17//! assert_eq!(var0.as_str(), MyEnum::Variant0.as_str())
18//! ```
19//!
20//! Note, due to how we assemble some strings at compile time you'll see some constants that you likely never need to interact with.
21//! You can just throw the enum in its own module to avoid seeing them since they're private visibility.
22
23#[cfg(feature = "serde")]
24pub use serde;
25
26#[cfg(feature = "strum")]
27pub use strum;
28
29#[cfg(feature = "phf")]
30pub use phf;
31
32#[cfg(feature = "phf")]
33#[macro_export]
34macro_rules! str_enum_try_from_str {
35    (#[phf] $(#[error_type($error_ty:ident)])? $(#[derive($($derive_trait:ident),* $(,)?)])? $(#[repr($repr:ty)])? $vis:vis enum $ty:ident { $($variant:ident $(= $variant_repr:literal)? => $val:literal $(($($other_valid:literal),* $(,)?))?),* $(,)? }) => {
36        impl $ty {
37            #[doc = "Compile time generated map from all valid string variants to their Variant"]
38            const PHF_MAP: $crate::phf::Map<&'static str, $ty> = $crate::phf::phf_map! {
39                $($val $($(| $other_valid )*)? => $ty::$variant,)*
40            };
41
42            #[doc = "Try to generate `Self` from an &str, using `Self::PHF_MAP`"]
43            pub fn try_from_str(s: &str) -> Option<Self> {
44                Self::PHF_MAP.get(s).copied()
45            }
46        }
47    };
48    ($(#[error_type($error_ty:ident)])? $(#[derive($($derive_trait:ident),* $(,)?)])? $(#[repr($repr:ty)])? $vis:vis enum $ty:ident { $($variant:ident $(= $variant_repr:literal)? => $val:literal $(($($other_valid:literal),* $(,)?))?),* $(,)? }) => {
49        impl $ty {
50            #[doc = "Try to generate `Self` from an &str by matching on all valid values.\nNote: If you have many variants you may want to enable the `phf` feature and add the `#[phf]` attribute."]
51            pub fn try_from_str(s: &str) -> Option<Self> {
52                match s {
53                    $($val $($(|$other_valid)*)? => Some(Self::$variant),)*
54                    _ => None,
55                }
56            }
57        }
58    }
59}
60
61#[cfg(not(feature = "phf"))]
62#[macro_export]
63macro_rules! str_enum_try_from_str {
64    (#[phf] $(#[error_type($error_ty:ident)])? $(#[derive($($derive_trait:ident),* $(,)?)])? $(#[repr($repr:ty)])? $vis:vis enum $ty:ident { $($variant:ident $(= $variant_repr:literal)? => $val:literal $(($($other_valid:literal),* $(,)?))?),* $(,)? }) => {
65        impl $ty {
66            #[doc = "Try to generate `Self` from an &str by matching on all valid values.\nNote: If you have many variants you may want to enable the `phf` feature and add the `#[phf]` attribute."]
67            pub fn try_from_str(s: &str) -> Option<Self> {
68                match s {
69                    $($val $($(|$other_valid)*)? => Some(Self::$variant),)*
70                    _ => None,
71                }
72            }
73        }
74    };
75    ($(#[error_type($error_ty:ident)])? $(#[derive($($derive_trait:ident),* $(,)?)])? $(#[repr($repr:ty)])? $vis:vis enum $ty:ident { $($variant:ident $(= $variant_repr:literal)? => $val:literal $(($($other_valid:literal),* $(,)?))?),* $(,)? }) => {
76        impl $ty {
77            #[doc = "Try to generate `Self` from an &str by matching on all valid values.\nNote: If you have many variants you may want to enable the `phf` feature and add the `#[phf]` attribute."]
78            pub fn try_from_str(s: &str) -> Option<Self> {
79                match s {
80                    $($val $($(|$other_valid)*)? => Some(Self::$variant),)*
81                    _ => None,
82                }
83            }
84        }
85    }
86}
87
88#[macro_export]
89macro_rules! str_enum_base {
90    ($(#[error_type($error_ty:ident)])? $(#[derive($($derive_trait:ident),* $(,)?)])? $(#[repr($repr:ty)])? $vis:vis enum $ty:ident { $($variant:ident $(= $variant_repr:literal)? => $val:literal $(($($other_valid:literal),* $(,)?))?),* $(,)? }) => {
91        $(
92            #[derive($($derive_trait,)*)]
93        )?
94        $(
95            #[repr($repr)]
96        )?
97        $vis enum $ty {
98            $(
99                $variant $(= $variant_repr)?,
100            )*
101        }
102
103        impl $ty {
104            #[doc = "Collection of all variants in `Self`"]
105            pub const ALL_VARIANTS: &[Self] = &[$(Self::$variant,)*];
106            #[doc = "Number of variants in `Self`"]
107            pub const NUM_VARIANTS: usize = Self::ALL_VARIANTS.len();
108
109            pub const fn as_str(&self) -> &'static str {
110                match self {
111                    $(Self::$variant => $val,)*
112                }
113            }
114
115            #[doc = "All values of `Self`, does not include alternate spellings used for `Self::try_from_str`"]
116            pub const ALL_VALUES: &[&str] = &[$(Self::$variant.as_str(),)*];
117
118            #[doc = "Total length of `Self::ALL_VALUES + 1 byte separator. You do not need this."]
119            const ALL_VALUES_STR_LEN: usize = {
120                let mut len = 0usize;
121                let mut idx = 0usize;
122                while idx < Self::ALL_VALUES.len() {
123                    let value = Self::ALL_VALUES[idx];
124                    len += value.len() + 1;
125                    idx += 1
126                }
127                len - 1
128            };
129
130            #[doc = "Fixed size byte array of `Self::ALL_VALUES` joined by a comma separator. You do not need this."]
131            const ALL_VALUE_BYTES: [u8; Self::ALL_VALUES_STR_LEN] = {
132                let mut buf = [0u8; Self::ALL_VALUES_STR_LEN];
133                let mut idx = 0;
134                let mut buf_idx = 0;
135                while idx < Self::ALL_VALUES.len() {
136                    let value = Self::ALL_VALUES[idx];
137                    let mut value_idx = 0;
138                    while value_idx < value.len() {
139                        buf[buf_idx] = value.as_bytes()[value_idx];
140                        value_idx += 1;
141                        buf_idx += 1
142                    }
143
144                    if idx != Self::ALL_VALUES.len() - 1 {
145                        buf[buf_idx] = b',';
146                        buf_idx += 1;
147                    }
148                    idx += 1
149                }
150                buf
151            };
152
153            // we assemble this in a funny way due to issues with slicing in const
154            #[doc = "&'static str of `Self::ALL_VALUE_BYTES`"]
155            const ALL_VALUE_STR: &str = {
156                match str::from_utf8(&Self::ALL_VALUE_BYTES) {
157                    Ok(o) => o,
158                    Err(_) => panic!(),
159                }
160            };
161        }
162
163        impl $ty {
164            #[doc = "len() of this variant's str equivalent"]
165            pub const fn len(&self) -> usize {
166                self.as_str().len()
167            }
168
169            #[doc = "equivalent to str::is_empty"]
170            pub const fn is_empty(&self) -> bool {
171                self.as_str().is_empty()
172            }
173        }
174
175        $(
176            impl $ty {
177                #[doc = "Convert this enum into its repr"]
178                fn into_repr(self) -> $repr {
179                    self as $repr
180                }
181            }
182
183            impl From<$ty> for $repr {
184                fn from(v: $ty) -> $repr {
185                    v as $repr
186                }
187            }
188        )?
189
190        impl std::fmt::Display for $ty {
191            fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
192                <str as std::fmt::Display>::fmt(self.as_str(), fmt)
193            }
194        }
195
196        impl std::borrow::Borrow<str> for $ty {
197            fn borrow(&self) -> &str {
198                self.as_str()
199            }
200        }
201
202        impl std::hash::Hash for $ty {
203            fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
204                <str as std::hash::Hash>::hash(self.as_str(), state)
205            }
206        }
207
208        impl<'a> std::ops::Add<$ty> for std::borrow::Cow<'a, str> {
209            type Output = std::borrow::Cow<'a, str>;
210
211            fn add(self, rhs: $ty) -> std::borrow::Cow<'a, str> {
212                self.add(rhs.as_str())
213            }
214        }
215
216        impl std::ops::Add<$ty> for String {
217            type Output = String;
218
219            fn add(self, rhs: $ty) -> String {
220                self.add(rhs.as_str())
221            }
222        }
223
224        impl<'a> std::ops::AddAssign<$ty> for std::borrow::Cow<'a, str> {
225            fn add_assign(&mut self, rhs: $ty) {
226                self.add_assign(rhs.as_str())
227            }
228        }
229
230        impl std::ops::AddAssign<$ty> for String {
231            fn add_assign(&mut self, rhs: $ty) {
232                self.add_assign(rhs.as_str())
233            }
234        }
235
236        $crate::str_enum_base!(AsRef $ty, [str, std::ffi::OsStr, std::path::Path, [u8]]);
237
238        impl Extend<$ty> for String {
239            fn extend<I>(&mut self, iter: I) where I: IntoIterator<Item = $ty> {
240                iter.into_iter().for_each(move |s| self.push_str(s.as_str()))
241            }
242        }
243
244        $crate::str_enum_base!(From $ty, [std::sync::Arc<str>, Box<str>, std::rc::Rc<str>, String, Vec<u8>]);
245        $crate::str_enum_base!(From 'a $ty, [Box<dyn std::error::Error + 'a>, Box<dyn std::error::Error + Send + Sync + 'a>, std::borrow::Cow<'a, str>]);
246        $crate::str_enum_base!(FromIterator $ty, [Box<str>, String]);
247        $crate::str_enum_base!(FromIterator 'a $ty, [std::borrow::Cow<'a, str>]);
248
249        impl<I: std::slice::SliceIndex<str>> std::ops::Index<I> for $ty {
250            type Output = <I as std::slice::SliceIndex<str>>::Output;
251
252            fn index(&self, index: I) -> &<I as std::slice::SliceIndex<str>>::Output {
253                self.as_str().index(index)
254            }
255        }
256
257        $crate::str_enum_base!(PartialEq $ty, [std::ffi::OsStr, std::ffi::OsString, String, std::path::Path, std::path::PathBuf]);
258        $crate::str_enum_base!(PartialEq 'a $ty, [std::borrow::Cow<'a, str>]);
259
260        impl PartialEq<&str> for $ty {
261            fn eq(&self, rhs: &&str) -> bool {
262                self.as_str().eq(*rhs)
263            }
264        }
265
266        impl PartialEq<$ty> for &str {
267            fn eq(&self, rhs: &$ty) -> bool {
268                self.eq(&rhs.as_str())
269            }
270        }
271
272        impl PartialEq<str> for $ty {
273            fn eq(&self, rhs: &str) -> bool {
274                self.as_str().eq(rhs)
275            }
276        }
277
278        impl PartialEq<$ty> for str {
279            fn eq(&self, rhs: &$ty) -> bool {
280                self.eq(rhs.as_str())
281            }
282        }
283
284        $crate::str_enum_base!(PartialOrd $ty, [std::ffi::OsStr, std::ffi::OsString]);
285
286        impl PartialOrd<$ty> for str {
287            fn partial_cmp(&self, rhs: &$ty) -> Option<std::cmp::Ordering> {
288                self.partial_cmp(rhs.as_str())
289            }
290        }
291
292        impl PartialOrd<str> for $ty {
293            fn partial_cmp(&self, rhs: &str) -> Option<std::cmp::Ordering> {
294                self.as_str().partial_cmp(rhs)
295            }
296        }
297
298        impl PartialOrd<$ty> for &str {
299            fn partial_cmp(&self, rhs: &$ty) -> Option<std::cmp::Ordering> {
300                self.partial_cmp(&rhs.as_str())
301            }
302        }
303
304        impl PartialOrd<&str> for $ty {
305            fn partial_cmp(&self, rhs: &&str) -> Option<std::cmp::Ordering> {
306                self.as_str().partial_cmp(*rhs)
307            }
308        }
309
310        impl std::net::ToSocketAddrs for $ty {
311            type Iter = std::vec::IntoIter<std::net::SocketAddr>;
312
313            fn to_socket_addrs(&self) -> std::io::Result<std::vec::IntoIter<std::net::SocketAddr>> {
314                <str as std::net::ToSocketAddrs>::to_socket_addrs(self.as_str())
315            }
316        }
317
318    };
319    (AsRef $self:ident, [$($other:ty),*]) => {
320        $(
321            impl AsRef<$other> for $self {
322                fn as_ref(&self) -> &$other {
323                    <str as AsRef<$other>>::as_ref(self.as_str())
324                }
325            }
326        )*
327    };
328    (From $self:ident, [$($other:ty),*]) => {
329        $(
330            impl From<$self> for $other {
331                fn from(val: $self) -> $other {
332                    From::from(val.as_str())
333                }
334            }
335        )*
336    };
337    (From 'a $self:ident, [$($other:ty),*]) => {
338        $(
339            impl<'a> From<$self> for $other {
340                fn from(val: $self) -> $other {
341                    From::from(val.as_str())
342                }
343            }
344        )*
345    };
346    (FromIterator $self:ident, [$($other:ty),*]) => {
347        $(
348            impl std::iter::FromIterator<$self> for $other {
349                fn from_iter<T>(iter: T) -> $other
350                where
351                    T: IntoIterator<Item = $self>
352                {
353                    <$other as std::iter::FromIterator<&'static str>>::from_iter(iter.into_iter().map(|s| s.as_str()))
354                }
355            }
356        )*
357    };
358    (FromIterator 'a $self:ident, [$($other:ty),*]) => {
359        $(
360            impl<'a> std::iter::FromIterator<$self> for $other {
361                fn from_iter<T>(iter: T) -> $other
362                where
363                    T: IntoIterator<Item = $self>
364                {
365                    <$other as std::iter::FromIterator<&'static str>>::from_iter(iter.into_iter().map(|s| s.as_str()))
366                }
367            }
368        )*
369    };
370    (PartialEq $self:ident, [$($other:ty),*]) => {
371        $(
372            impl PartialEq<$self> for $other {
373                fn eq(&self, rhs: &$self) -> bool {
374                    self.eq(rhs.as_str())
375                }
376            }
377
378            impl PartialEq<$other> for $self {
379                fn eq(&self, rhs: &$other) -> bool {
380                    self.as_str().eq(rhs)
381                }
382            }
383        )*
384    };
385    (PartialEq 'a $self:ident, [$($other:ty),*]) => {
386        $(
387            impl<'a> PartialEq<$self> for $other {
388                fn eq(&self, rhs: &$self) -> bool {
389                    self.eq(rhs.as_str())
390                }
391            }
392
393            impl<'a> PartialEq<$other> for $self {
394                fn eq(&self, rhs: &$other) -> bool {
395                    self.as_str().eq(rhs)
396                }
397            }
398        )*
399    };
400    (PartialOrd $self:ident, [$($other:ty),*]) => {
401        $(
402            impl PartialOrd<$self> for $other {
403                fn partial_cmp(&self, rhs: &$self) -> Option<std::cmp::Ordering> {
404                    self.partial_cmp(rhs.as_str())
405                }
406            }
407        )*
408    };
409    (PartialOrd 'a $self:ident, [$($other:ty),*]) => {
410        $(
411            impl<'a> PartialOrd<$self> for $other {
412                fn partial_cmp(&self, rhs: &$self) -> Option<std::cmp::Ordering> {
413                    self.partial_cmp(rhs.as_str())
414                }
415            }
416        )*
417    };
418    (FromStr $(#[error_type($error_ty:ident)])? $(#[derive($($derive_trait:ident),* $(,)?)])? $(#[repr($repr:ty)])? $vis:vis enum $ty:ident { $($variant:ident $(= $variant_repr:literal)? => $val:literal $(($($other_valid:literal),* $(,)?))?),* $(,)? }) => {
419        $(
420            #[derive(Debug, Clone, Copy, Default)]
421            $vis struct $error_ty;
422
423            impl $error_ty {
424                #[doc = "Length of Self's error string. You do not need this."]
425                const EXPECTED_STR_LEN: usize = "expected one of [".len() + "]".len() + $ty::ALL_VALUES_STR_LEN;
426                #[doc = "Bytes of Self's error string. You do not need this."]
427                const EXPECTED_STR_BYTES: [u8; Self::EXPECTED_STR_LEN] = {
428                    let mut buf = [0u8; Self::EXPECTED_STR_LEN];
429                    let mut idx = 0;
430
431                    let first_part = b"expected one of [";
432
433                    while idx < first_part.len() {
434                        buf[idx] = first_part[idx];
435                        idx += 1
436                    }
437
438                    while idx < first_part.len() + $ty::ALL_VALUES_STR_LEN {
439                        buf[idx] = $ty::ALL_VALUE_BYTES[idx - first_part.len()];
440                        idx +=1
441                    }
442                    buf[Self::EXPECTED_STR_LEN - 1] = b']';
443
444                    buf
445                };
446                #[doc = "&'static str of `Self::EXPECTED_STR_BYTES`. You do not need this."]
447                const EXPECTED_STR: &str = {
448                    match str::from_utf8(&Self::EXPECTED_STR_BYTES) {
449                        Ok(o) => o,
450                        Err(_) => panic!(),
451                    }
452                };
453            }
454
455            impl std::fmt::Display for $error_ty {
456                fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
457                    <str as std::fmt::Display>::fmt(Self::EXPECTED_STR, fmt)
458                }
459            }
460
461            impl std::error::Error for $error_ty {}
462
463            impl std::str::FromStr for $ty {
464                type Err = $error_ty;
465
466                fn from_str(s: &str) -> Result<$ty, Self::Err> {
467                    match Self::try_from_str(s) {
468                        Some(variant) => Ok(variant),
469                        None => Err($error_ty)
470                    }
471                }
472            }
473
474            impl TryFrom<&str> for $ty {
475                type Error = $error_ty;
476
477                fn try_from(s: &str) -> Result<$ty, Self::Error> {
478                    match Self::try_from_str(s) {
479                        Some(variant) => Ok(variant),
480                        None => Err($error_ty)
481                    }
482                }
483            }
484
485            impl TryFrom<String> for $ty {
486                type Error = $error_ty;
487
488                fn try_from(s: String) -> Result<$ty, Self::Error> {
489                    match Self::try_from_str(&s) {
490                        Some(variant) => Ok(variant),
491                        None => Err($error_ty)
492                    }
493                }
494            }
495
496            impl<'a> TryFrom<&'a std::ffi::OsStr> for $ty {
497                type Error = $crate::Utf8EnumError<$error_ty>;
498
499                fn try_from(value: &'a std::ffi::OsStr) -> Result<$ty, Self::Error> {
500                    <&'a str as TryFrom<&'a std::ffi::OsStr>>::try_from(value)
501                    .map_err($crate::Utf8EnumError::Utf8)
502                    .and_then(|s| $ty::try_from(s).map_err($crate::Utf8EnumError::InvalidVariant))
503                }
504            }
505        )?
506    }
507}
508
509#[cfg(feature = "strum")]
510#[macro_export]
511macro_rules! str_enum_strum {
512    ($(#[error_type($error_ty:ident)])? $(#[derive($($derive_trait:ident),* $(,)?)])? $(#[repr($repr:ty)])? $vis:vis enum $ty:ident { $($variant:ident $(= $variant_repr:literal)? => $val:literal $(($($other_valid:literal),* $(,)?))?),* $(,)? }) => {
513        impl $crate::strum::EnumCount for $ty {
514            const COUNT: usize = $ty::ALL_VARIANTS.len();
515        }
516
517        $(
518            impl $crate::strum::IntoDiscriminant for $ty {
519                type Discriminant = $repr;
520
521                fn discriminant(&self) -> Self::Discriminant {
522                    self.into_repr()
523                }
524            }
525        )?
526
527        impl $crate::strum::IntoEnumIterator for $ty {
528            type Iterator = std::array::IntoIter<$ty, {$ty::NUM_VARIANTS}>;
529
530            fn iter() -> Self::Iterator {
531                [$(Self::$variant,)*].into_iter()
532            }
533        }
534
535        impl $crate::strum::VariantArray for $ty {
536            const VARIANTS: &'static [Self] = Self::ALL_VARIANTS;
537        }
538
539        impl $crate::strum::VariantIterator for $ty {
540            type Iterator = std::array::IntoIter<$ty, {$ty::NUM_VARIANTS}>;
541
542            fn iter() -> Self::Iterator {
543                [$(Self::$variant,)*].into_iter()
544            }
545        }
546
547        impl $crate::strum::VariantNames for $ty {
548            const VARIANTS: &'static [&'static str] = &[$(stringify!($variant),)*];
549        }
550
551        impl $crate::strum::VariantMetadata for $ty {
552            const VARIANT_COUNT: usize = Self::ALL_VARIANTS.len();
553            const VARIANT_NAMES: &'static [&'static str] = &[$(stringify!($variant),)*];
554
555            fn variant_name(&self) -> &'static str {
556                match self {
557                    $(Self::$variant => stringify!($variant),)*
558                }
559            }
560        }
561    };
562}
563
564#[macro_export]
565#[cfg(not(feature = "strum"))]
566macro_rules! str_enum_strum {
567    ($(#[error_type($error_ty:ident)])? $(#[derive($($derive_trait:ident),* $(,)?)])? $(#[repr($repr:ty)])? $vis:vis enum $ty:ident { $($variant:ident $(= $variant_repr:literal)? => $val:literal $(($($other_valid:literal),* $(,)?))?),* $(,)? }) => {};
568}
569
570#[macro_export]
571#[cfg(feature = "serde")]
572macro_rules! str_enum_serde {
573    ($(#[error_type($error_ty:ident)])? $(#[derive($($derive_trait:ident),* $(,)?)])? $(#[repr($repr:ty)])? $vis:vis enum $ty:ident { $($variant:ident $(= $variant_repr:literal)? => $val:literal $(($($other_valid:literal),* $(,)?))?),* $(,)? }) => {
574        impl $ty {
575            #[doc = "Length of Self's serde deserialize error. You do not need this."]
576            const SERDE_EXPECTED_STR_LEN: usize = "one of [".len() + "]".len() + Self::ALL_VALUES_STR_LEN;
577            #[doc = "Bytes of Self's serde deserialize error. You do not need this."]
578            const SERDE_EXPECTED_STR_BYTES: [u8; Self::SERDE_EXPECTED_STR_LEN] = {
579                let mut buf = [0u8; Self::SERDE_EXPECTED_STR_LEN];
580                let mut idx = 0;
581
582                let first_part = b"one of [";
583
584                while idx < first_part.len() {
585                    buf[idx] = first_part[idx];
586                    idx += 1
587                }
588
589                while idx < first_part.len() + Self::ALL_VALUES_STR_LEN {
590                    buf[idx] = Self::ALL_VALUE_BYTES[idx - first_part.len()];
591                    idx +=1
592                }
593                buf[Self::SERDE_EXPECTED_STR_LEN - 1] = b']';
594
595                buf
596            };
597
598            #[doc = "&'static str of `Self::SERDE_EXPECTED_STR_BYTES`. You do not need this."]
599            const SERDE_EXPECTED_STR: &str = {
600                match str::from_utf8(&Self::SERDE_EXPECTED_STR_BYTES) {
601                    Ok(o) => o,
602                    Err(_) => panic!(),
603                }
604            };
605        }
606
607
608        $(
609            impl $crate::serde::de::Expected for $error_ty {
610                fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
611                    <str as std::fmt::Display>::fmt($ty::SERDE_EXPECTED_STR, formatter)
612                }
613            }
614        )?
615
616        impl $crate::serde::Serialize for $ty {
617            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
618            where
619                S: $crate::serde::Serializer,
620            {
621                self.as_str().serialize(serializer)
622            }
623        }
624
625        impl<'de> $crate::serde::Deserialize<'de> for $ty {
626            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
627            where
628                D: $crate::serde::Deserializer<'de>,
629            {
630                let val = <std::borrow::Cow<'_, str> as $crate::serde::Deserialize>::deserialize(deserializer)?;
631                $ty::try_from_str(&val).ok_or_else(|| $crate::serde::de::Error::invalid_value($crate::serde::de::Unexpected::Str(&val), &$ty::SERDE_EXPECTED_STR))
632            }
633        }
634    };
635}
636
637#[macro_export]
638#[cfg(not(feature = "serde"))]
639macro_rules! str_enum_serde {
640    ($(#[error_type($error_ty:ident)])? $(#[derive($($derive_trait:ident),* $(,)?)])? $(#[repr($repr:ty)])? $vis:vis enum $ty:ident { $($variant:ident $(= $variant_repr:literal)? => $val:literal $(($($other_valid:literal),* $(,)?))?),* $(,)? }) => {};
641}
642
643#[macro_export]
644macro_rules! str_enum {
645    ($(#[error_type($error_ty:ident)])? $(#[derive($($derive_trait:ident),* $(,)?)])? $(#[repr($repr:ty)])? $vis:vis enum $ty:ident { $($variant:ident $(= $variant_repr:literal)? => $val:literal $(($($other_valid:literal),* $(,)?))?),* $(,)? }) => {
646        $crate::str_enum_base!(
647            $(#[error_type($error_ty)])?
648            $(#[derive($($derive_trait,)*)])?
649            $(#[repr($repr)])?
650            $vis enum $ty {
651                $($variant $(= $variant_repr)? => $val $(($($other_valid),*))?,)*
652            }
653        );
654
655        $crate::str_enum_strum!(
656            $(#[error_type($error_ty)])?
657            $(#[derive($($derive_trait,)*)])?
658            $(#[repr($repr)])?
659            $vis enum $ty {
660                $($variant $(= $variant_repr)? => $val $(($($other_valid),*))?,)*
661            }
662        );
663
664        $crate::str_enum_serde!(
665            $(#[error_type($error_ty)])?
666            $(#[derive($($derive_trait,)*)])?
667            $(#[repr($repr)])?
668            $vis enum $ty {
669                $($variant $(= $variant_repr)? => $val $(($($other_valid),*))?,)*
670            }
671        );
672
673        $crate::str_enum_try_from_str!{
674            $(#[error_type($error_ty)])?
675            $(#[derive($($derive_trait,)*)])?
676            $(#[repr($repr)])?
677            $vis enum $ty {
678                $($variant $(= $variant_repr)? => $val $(($($other_valid),*))?,)*
679            }
680        }
681
682        $crate::str_enum_base!(FromStr
683            $(#[error_type($error_ty)])?
684            $(#[derive($($derive_trait,)*)])?
685            $(#[repr($repr)])?
686            $vis enum $ty {
687                $($variant $(= $variant_repr)? => $val $(($($other_valid),*))?,)*
688            }
689        );
690    };
691    (#[phf] $(#[error_type($error_ty:ident)])? $(#[derive($($derive_trait:ident),* $(,)?)])? $(#[repr($repr:ty)])? $vis:vis enum $ty:ident { $($variant:ident $(= $variant_repr:literal)? => $val:literal $(($($other_valid:literal),* $(,)?))?),* $(,)? }) => {
692        $crate::str_enum_base!(
693            $(#[error_type($error_ty)])?
694            $(#[derive($($derive_trait,)*)])?
695            $(#[repr($repr)])?
696            $vis enum $ty {
697                $($variant $(= $variant_repr)? => $val $(($($other_valid),*))?,)*
698            }
699        );
700
701        $crate::str_enum_strum!(
702            $(#[error_type($error_ty)])?
703            $(#[derive($($derive_trait,)*)])?
704            $(#[repr($repr)])?
705            $vis enum $ty {
706                $($variant $(= $variant_repr)? => $val $(($($other_valid),*))?,)*
707            }
708        );
709
710        $crate::str_enum_serde!(
711            $(#[error_type($error_ty)])?
712            $(#[derive($($derive_trait,)*)])?
713            $(#[repr($repr)])?
714            $vis enum $ty {
715                $($variant $(= $variant_repr)? => $val $(($($other_valid),*))?,)*
716            }
717        );
718
719        $crate::str_enum_try_from_str!{
720            #[phf]
721            $(#[error_type($error_ty)])?
722            $(#[derive($($derive_trait,)*)])?
723            $(#[repr($repr)])?
724            $vis enum $ty {
725                $($variant $(= $variant_repr)? => $val $(($($other_valid),*))?,)*
726            }
727        }
728
729        $crate::str_enum_base!(FromStr
730            $(#[error_type($error_ty)])?
731            $(#[derive($($derive_trait,)*)])?
732            $(#[repr($repr)])?
733            $vis enum $ty {
734                $($variant $(= $variant_repr)? => $val $(($($other_valid),*))?,)*
735            }
736        );
737    };
738}
739
740#[derive(Debug, Clone, Copy, PartialEq, Eq)]
741pub enum Utf8EnumError<E> {
742    Utf8(std::str::Utf8Error),
743    InvalidVariant(E),
744}
745
746impl<E> std::fmt::Display for Utf8EnumError<E>
747where
748    E: std::fmt::Display,
749{
750    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
751        match self {
752            Utf8EnumError::Utf8(utf8_error) => utf8_error.fmt(f),
753            Utf8EnumError::InvalidVariant(variant_error) => variant_error.fmt(f),
754        }
755    }
756}
757
758impl<E> std::error::Error for Utf8EnumError<E> where E: std::error::Error {}