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
170        $(
171            impl $ty {
172                #[doc = "Convert this enum into its repr"]
173                fn into_repr(self) -> $repr {
174                    self as $repr
175                }
176            }
177
178            impl From<$ty> for $repr {
179                fn from(v: $ty) -> $repr {
180                    v as $repr
181                }
182            }
183        )?
184
185        impl std::fmt::Display for $ty {
186            fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187                <str as std::fmt::Display>::fmt(self.as_str(), fmt)
188            }
189        }
190
191        impl std::borrow::Borrow<str> for $ty {
192            fn borrow(&self) -> &str {
193                self.as_str()
194            }
195        }
196
197        impl std::hash::Hash for $ty {
198            fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
199                <str as std::hash::Hash>::hash(self.as_str(), state)
200            }
201        }
202
203        impl<'a> std::ops::Add<$ty> for std::borrow::Cow<'a, str> {
204            type Output = std::borrow::Cow<'a, str>;
205
206            fn add(self, rhs: $ty) -> std::borrow::Cow<'a, str> {
207                self.add(rhs.as_str())
208            }
209        }
210
211        impl std::ops::Add<$ty> for String {
212            type Output = String;
213
214            fn add(self, rhs: $ty) -> String {
215                self.add(rhs.as_str())
216            }
217        }
218
219        impl<'a> std::ops::AddAssign<$ty> for std::borrow::Cow<'a, str> {
220            fn add_assign(&mut self, rhs: $ty) {
221                self.add_assign(rhs.as_str())
222            }
223        }
224
225        impl std::ops::AddAssign<$ty> for String {
226            fn add_assign(&mut self, rhs: $ty) {
227                self.add_assign(rhs.as_str())
228            }
229        }
230
231        $crate::str_enum_base!(AsRef $ty, [str, std::ffi::OsStr, std::path::Path, [u8]]);
232
233        impl Extend<$ty> for String {
234            fn extend<I>(&mut self, iter: I) where I: IntoIterator<Item = $ty> {
235                iter.into_iter().for_each(move |s| self.push_str(s.as_str()))
236            }
237        }
238
239        $crate::str_enum_base!(From $ty, [std::sync::Arc<str>, Box<str>, std::rc::Rc<str>, String, Vec<u8>]);
240        $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>]);
241        $crate::str_enum_base!(FromIterator $ty, [Box<str>, String]);
242        $crate::str_enum_base!(FromIterator 'a $ty, [std::borrow::Cow<'a, str>]);
243
244        impl<I: std::slice::SliceIndex<str>> std::ops::Index<I> for $ty {
245            type Output = <I as std::slice::SliceIndex<str>>::Output;
246
247            fn index(&self, index: I) -> &<I as std::slice::SliceIndex<str>>::Output {
248                self.as_str().index(index)
249            }
250        }
251
252        $crate::str_enum_base!(PartialEq $ty, [std::ffi::OsStr, std::ffi::OsString, String, std::path::Path, std::path::PathBuf]);
253        $crate::str_enum_base!(PartialEq 'a $ty, [std::borrow::Cow<'a, str>]);
254
255        impl PartialEq<&str> for $ty {
256            fn eq(&self, rhs: &&str) -> bool {
257                self.as_str().eq(*rhs)
258            }
259        }
260
261        impl PartialEq<$ty> for &str {
262            fn eq(&self, rhs: &$ty) -> bool {
263                self.eq(&rhs.as_str())
264            }
265        }
266
267        impl PartialEq<str> for $ty {
268            fn eq(&self, rhs: &str) -> bool {
269                self.as_str().eq(rhs)
270            }
271        }
272
273        impl PartialEq<$ty> for str {
274            fn eq(&self, rhs: &$ty) -> bool {
275                self.eq(rhs.as_str())
276            }
277        }
278
279        $crate::str_enum_base!(PartialOrd $ty, [std::ffi::OsStr, std::ffi::OsString]);
280
281        impl PartialOrd<$ty> for str {
282            fn partial_cmp(&self, rhs: &$ty) -> Option<std::cmp::Ordering> {
283                self.partial_cmp(rhs.as_str())
284            }
285        }
286
287        impl PartialOrd<str> for $ty {
288            fn partial_cmp(&self, rhs: &str) -> Option<std::cmp::Ordering> {
289                self.as_str().partial_cmp(rhs)
290            }
291        }
292
293        impl PartialOrd<$ty> for &str {
294            fn partial_cmp(&self, rhs: &$ty) -> Option<std::cmp::Ordering> {
295                self.partial_cmp(&rhs.as_str())
296            }
297        }
298
299        impl PartialOrd<&str> for $ty {
300            fn partial_cmp(&self, rhs: &&str) -> Option<std::cmp::Ordering> {
301                self.as_str().partial_cmp(*rhs)
302            }
303        }
304
305        impl std::net::ToSocketAddrs for $ty {
306            type Iter = std::vec::IntoIter<std::net::SocketAddr>;
307
308            fn to_socket_addrs(&self) -> std::io::Result<std::vec::IntoIter<std::net::SocketAddr>> {
309                <str as std::net::ToSocketAddrs>::to_socket_addrs(self.as_str())
310            }
311        }
312
313    };
314    (AsRef $self:ident, [$($other:ty),*]) => {
315        $(
316            impl AsRef<$other> for $self {
317                fn as_ref(&self) -> &$other {
318                    <str as AsRef<$other>>::as_ref(self.as_str())
319                }
320            }
321        )*
322    };
323    (From $self:ident, [$($other:ty),*]) => {
324        $(
325            impl From<$self> for $other {
326                fn from(val: $self) -> $other {
327                    From::from(val.as_str())
328                }
329            }
330        )*
331    };
332    (From 'a $self:ident, [$($other:ty),*]) => {
333        $(
334            impl<'a> From<$self> for $other {
335                fn from(val: $self) -> $other {
336                    From::from(val.as_str())
337                }
338            }
339        )*
340    };
341    (FromIterator $self:ident, [$($other:ty),*]) => {
342        $(
343            impl std::iter::FromIterator<$self> for $other {
344                fn from_iter<T>(iter: T) -> $other
345                where
346                    T: IntoIterator<Item = $self>
347                {
348                    <$other as std::iter::FromIterator<&'static str>>::from_iter(iter.into_iter().map(|s| s.as_str()))
349                }
350            }
351        )*
352    };
353    (FromIterator 'a $self:ident, [$($other:ty),*]) => {
354        $(
355            impl<'a> std::iter::FromIterator<$self> for $other {
356                fn from_iter<T>(iter: T) -> $other
357                where
358                    T: IntoIterator<Item = $self>
359                {
360                    <$other as std::iter::FromIterator<&'static str>>::from_iter(iter.into_iter().map(|s| s.as_str()))
361                }
362            }
363        )*
364    };
365    (PartialEq $self:ident, [$($other:ty),*]) => {
366        $(
367            impl PartialEq<$self> for $other {
368                fn eq(&self, rhs: &$self) -> bool {
369                    self.eq(rhs.as_str())
370                }
371            }
372
373            impl PartialEq<$other> for $self {
374                fn eq(&self, rhs: &$other) -> bool {
375                    self.as_str().eq(rhs)
376                }
377            }
378        )*
379    };
380    (PartialEq 'a $self:ident, [$($other:ty),*]) => {
381        $(
382            impl<'a> PartialEq<$self> for $other {
383                fn eq(&self, rhs: &$self) -> bool {
384                    self.eq(rhs.as_str())
385                }
386            }
387
388            impl<'a> PartialEq<$other> for $self {
389                fn eq(&self, rhs: &$other) -> bool {
390                    self.as_str().eq(rhs)
391                }
392            }
393        )*
394    };
395    (PartialOrd $self:ident, [$($other:ty),*]) => {
396        $(
397            impl PartialOrd<$self> for $other {
398                fn partial_cmp(&self, rhs: &$self) -> Option<std::cmp::Ordering> {
399                    self.partial_cmp(rhs.as_str())
400                }
401            }
402        )*
403    };
404    (PartialOrd 'a $self:ident, [$($other:ty),*]) => {
405        $(
406            impl<'a> PartialOrd<$self> for $other {
407                fn partial_cmp(&self, rhs: &$self) -> Option<std::cmp::Ordering> {
408                    self.partial_cmp(rhs.as_str())
409                }
410            }
411        )*
412    };
413    (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),* $(,)?))?),* $(,)? }) => {
414        $(
415            #[derive(Debug, Clone, Copy, Default)]
416            $vis struct $error_ty;
417
418            impl $error_ty {
419                #[doc = "Length of Self's error string. You do not need this."]
420                const EXPECTED_STR_LEN: usize = "expected one of [".len() + "]".len() + $ty::ALL_VALUES_STR_LEN;
421                #[doc = "Bytes of Self's error string. You do not need this."]
422                const EXPECTED_STR_BYTES: [u8; Self::EXPECTED_STR_LEN] = {
423                    let mut buf = [0u8; Self::EXPECTED_STR_LEN];
424                    let mut idx = 0;
425
426                    let first_part = b"expected one of [";
427
428                    while idx < first_part.len() {
429                        buf[idx] = first_part[idx];
430                        idx += 1
431                    }
432
433                    while idx < first_part.len() + $ty::ALL_VALUES_STR_LEN {
434                        buf[idx] = $ty::ALL_VALUE_BYTES[idx - first_part.len()];
435                        idx +=1
436                    }
437                    buf[Self::EXPECTED_STR_LEN - 1] = b']';
438
439                    buf
440                };
441                #[doc = "&'static str of `Self::EXPECTED_STR_BYTES`. You do not need this."]
442                const EXPECTED_STR: &str = {
443                    match str::from_utf8(&Self::EXPECTED_STR_BYTES) {
444                        Ok(o) => o,
445                        Err(_) => panic!(),
446                    }
447                };
448            }
449
450            impl std::fmt::Display for $error_ty {
451                fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
452                    <str as std::fmt::Display>::fmt(Self::EXPECTED_STR, fmt)
453                }
454            }
455
456            impl std::error::Error for $error_ty {}
457
458            impl std::str::FromStr for $ty {
459                type Err = $error_ty;
460
461                fn from_str(s: &str) -> Result<$ty, Self::Err> {
462                    match Self::try_from_str(s) {
463                        Some(variant) => Ok(variant),
464                        None => Err($error_ty)
465                    }
466                }
467            }
468
469            impl TryFrom<&str> for $ty {
470                type Error = $error_ty;
471
472                fn try_from(s: &str) -> Result<$ty, Self::Error> {
473                    match Self::try_from_str(s) {
474                        Some(variant) => Ok(variant),
475                        None => Err($error_ty)
476                    }
477                }
478            }
479
480            impl TryFrom<String> for $ty {
481                type Error = $error_ty;
482
483                fn try_from(s: String) -> Result<$ty, Self::Error> {
484                    match Self::try_from_str(&s) {
485                        Some(variant) => Ok(variant),
486                        None => Err($error_ty)
487                    }
488                }
489            }
490
491            impl<'a> TryFrom<&'a std::ffi::OsStr> for $ty {
492                type Error = $crate::Utf8EnumError<$error_ty>;
493
494                fn try_from(value: &'a std::ffi::OsStr) -> Result<$ty, Self::Error> {
495                    <&'a str as TryFrom<&'a std::ffi::OsStr>>::try_from(value)
496                    .map_err($crate::Utf8EnumError::Utf8)
497                    .and_then(|s| $ty::try_from(s).map_err($crate::Utf8EnumError::InvalidVariant))
498                }
499            }
500        )?
501    }
502}
503
504#[cfg(feature = "strum")]
505#[macro_export]
506macro_rules! str_enum_strum {
507    ($(#[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),* $(,)?))?),* $(,)? }) => {
508        impl $crate::strum::EnumCount for $ty {
509            const COUNT: usize = $ty::ALL_VARIANTS.len();
510        }
511
512        $(
513            impl $crate::strum::IntoDiscriminant for $ty {
514                type Discriminant = $repr;
515
516                fn discriminant(&self) -> Self::Discriminant {
517                    self.into_repr()
518                }
519            }
520        )?
521
522        impl $crate::strum::IntoEnumIterator for $ty {
523            type Iterator = std::array::IntoIter<$ty, {$ty::NUM_VARIANTS}>;
524
525            fn iter() -> Self::Iterator {
526                [$(Self::$variant,)*].into_iter()
527            }
528        }
529
530        impl $crate::strum::VariantArray for $ty {
531            const VARIANTS: &'static [Self] = Self::ALL_VARIANTS;
532        }
533
534        impl $crate::strum::VariantIterator for $ty {
535            type Iterator = std::array::IntoIter<$ty, {$ty::NUM_VARIANTS}>;
536
537            fn iter() -> Self::Iterator {
538                [$(Self::$variant,)*].into_iter()
539            }
540        }
541
542        impl $crate::strum::VariantNames for $ty {
543            const VARIANTS: &'static [&'static str] = &[$(stringify!($variant),)*];
544        }
545
546        impl $crate::strum::VariantMetadata for $ty {
547            const VARIANT_COUNT: usize = Self::ALL_VARIANTS.len();
548            const VARIANT_NAMES: &'static [&'static str] = &[$(stringify!($variant),)*];
549
550            fn variant_name(&self) -> &'static str {
551                match self {
552                    $(Self::$variant => stringify!($variant),)*
553                }
554            }
555        }
556    };
557}
558
559#[macro_export]
560#[cfg(not(feature = "strum"))]
561macro_rules! str_enum_strum {
562    ($(#[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),* $(,)?))?),* $(,)? }) => {};
563}
564
565#[macro_export]
566#[cfg(feature = "serde")]
567macro_rules! str_enum_serde {
568    ($(#[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),* $(,)?))?),* $(,)? }) => {
569        impl $ty {
570            #[doc = "Length of Self's serde deserialize error. You do not need this."]
571            const SERDE_EXPECTED_STR_LEN: usize = "one of [".len() + "]".len() + Self::ALL_VALUES_STR_LEN;
572            #[doc = "Bytes of Self's serde deserialize error. You do not need this."]
573            const SERDE_EXPECTED_STR_BYTES: [u8; Self::SERDE_EXPECTED_STR_LEN] = {
574                let mut buf = [0u8; Self::SERDE_EXPECTED_STR_LEN];
575                let mut idx = 0;
576
577                let first_part = b"one of [";
578
579                while idx < first_part.len() {
580                    buf[idx] = first_part[idx];
581                    idx += 1
582                }
583
584                while idx < first_part.len() + Self::ALL_VALUES_STR_LEN {
585                    buf[idx] = Self::ALL_VALUE_BYTES[idx - first_part.len()];
586                    idx +=1
587                }
588                buf[Self::SERDE_EXPECTED_STR_LEN - 1] = b']';
589
590                buf
591            };
592
593            #[doc = "&'static str of `Self::SERDE_EXPECTED_STR_BYTES`. You do not need this."]
594            const SERDE_EXPECTED_STR: &str = {
595                match str::from_utf8(&Self::SERDE_EXPECTED_STR_BYTES) {
596                    Ok(o) => o,
597                    Err(_) => panic!(),
598                }
599            };
600        }
601
602
603        $(
604            impl $crate::serde::de::Expected for $error_ty {
605                fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
606                    <str as std::fmt::Display>::fmt($ty::SERDE_EXPECTED_STR, formatter)
607                }
608            }
609        )?
610
611        impl $crate::serde::Serialize for $ty {
612            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
613            where
614                S: $crate::serde::Serializer,
615            {
616                self.as_str().serialize(serializer)
617            }
618        }
619
620        impl<'de> $crate::serde::Deserialize<'de> for $ty {
621            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
622            where
623                D: $crate::serde::Deserializer<'de>,
624            {
625                let val = <std::borrow::Cow<'_, str> as $crate::serde::Deserialize>::deserialize(deserializer)?;
626                $ty::try_from_str(&val).ok_or_else(|| $crate::serde::de::Error::invalid_value($crate::serde::de::Unexpected::Str(&val), &$ty::SERDE_EXPECTED_STR))
627            }
628        }
629    };
630}
631
632#[macro_export]
633#[cfg(not(feature = "serde"))]
634macro_rules! str_enum_serde {
635    ($(#[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),* $(,)?))?),* $(,)? }) => {};
636}
637
638#[macro_export]
639macro_rules! str_enum {
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        $crate::str_enum_base!(
642            $(#[error_type($error_ty)])?
643            $(#[derive($($derive_trait,)*)])?
644            $(#[repr($repr)])?
645            $vis enum $ty {
646                $($variant $(= $variant_repr)? => $val $(($($other_valid),*))?,)*
647            }
648        );
649
650        $crate::str_enum_strum!(
651            $(#[error_type($error_ty)])?
652            $(#[derive($($derive_trait,)*)])?
653            $(#[repr($repr)])?
654            $vis enum $ty {
655                $($variant $(= $variant_repr)? => $val $(($($other_valid),*))?,)*
656            }
657        );
658
659        $crate::str_enum_serde!(
660            $(#[error_type($error_ty)])?
661            $(#[derive($($derive_trait,)*)])?
662            $(#[repr($repr)])?
663            $vis enum $ty {
664                $($variant $(= $variant_repr)? => $val $(($($other_valid),*))?,)*
665            }
666        );
667
668        $crate::str_enum_try_from_str!{
669            $(#[error_type($error_ty)])?
670            $(#[derive($($derive_trait,)*)])?
671            $(#[repr($repr)])?
672            $vis enum $ty {
673                $($variant $(= $variant_repr)? => $val $(($($other_valid),*))?,)*
674            }
675        }
676
677        $crate::str_enum_base!(FromStr
678            $(#[error_type($error_ty)])?
679            $(#[derive($($derive_trait,)*)])?
680            $(#[repr($repr)])?
681            $vis enum $ty {
682                $($variant $(= $variant_repr)? => $val $(($($other_valid),*))?,)*
683            }
684        );
685    };
686    (#[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),* $(,)?))?),* $(,)? }) => {
687        $crate::str_enum_base!(
688            $(#[error_type($error_ty)])?
689            $(#[derive($($derive_trait,)*)])?
690            $(#[repr($repr)])?
691            $vis enum $ty {
692                $($variant $(= $variant_repr)? => $val $(($($other_valid),*))?,)*
693            }
694        );
695
696        $crate::str_enum_strum!(
697            $(#[error_type($error_ty)])?
698            $(#[derive($($derive_trait,)*)])?
699            $(#[repr($repr)])?
700            $vis enum $ty {
701                $($variant $(= $variant_repr)? => $val $(($($other_valid),*))?,)*
702            }
703        );
704
705        $crate::str_enum_serde!(
706            $(#[error_type($error_ty)])?
707            $(#[derive($($derive_trait,)*)])?
708            $(#[repr($repr)])?
709            $vis enum $ty {
710                $($variant $(= $variant_repr)? => $val $(($($other_valid),*))?,)*
711            }
712        );
713
714        $crate::str_enum_try_from_str!{
715            #[phf]
716            $(#[error_type($error_ty)])?
717            $(#[derive($($derive_trait,)*)])?
718            $(#[repr($repr)])?
719            $vis enum $ty {
720                $($variant $(= $variant_repr)? => $val $(($($other_valid),*))?,)*
721            }
722        }
723
724        $crate::str_enum_base!(FromStr
725            $(#[error_type($error_ty)])?
726            $(#[derive($($derive_trait,)*)])?
727            $(#[repr($repr)])?
728            $vis enum $ty {
729                $($variant $(= $variant_repr)? => $val $(($($other_valid),*))?,)*
730            }
731        );
732    };
733}
734
735#[derive(Debug, Clone, Copy, PartialEq, Eq)]
736pub enum Utf8EnumError<E> {
737    Utf8(std::str::Utf8Error),
738    InvalidVariant(E),
739}
740
741impl<E> std::fmt::Display for Utf8EnumError<E>
742where
743    E: std::fmt::Display,
744{
745    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
746        match self {
747            Utf8EnumError::Utf8(utf8_error) => utf8_error.fmt(f),
748            Utf8EnumError::InvalidVariant(variant_error) => variant_error.fmt(f),
749        }
750    }
751}
752
753impl<E> std::error::Error for Utf8EnumError<E> where E: std::error::Error {}