| //! # Strum |
| //! |
| //! Strum is a set of macros and traits for working with |
| //! enums and strings easier in Rust. |
| //! |
| |
| #![recursion_limit = "128"] |
| |
| extern crate proc_macro; |
| |
| mod helpers; |
| mod macros; |
| |
| use proc_macro2::TokenStream; |
| use std::env; |
| use syn::DeriveInput; |
| |
| fn debug_print_generated(ast: &DeriveInput, toks: &TokenStream) { |
| let debug = env::var("STRUM_DEBUG"); |
| if let Ok(s) = debug { |
| if s == "1" { |
| println!("{}", toks); |
| } |
| |
| if ast.ident == s { |
| println!("{}", toks); |
| } |
| } |
| } |
| |
| /// Converts strings to enum variants based on their name. |
| /// |
| /// auto-derives `std::str::FromStr` on the enum (for Rust 1.34 and above, `std::convert::TryFrom<&str>` |
| /// will be derived as well). Each variant of the enum will match on it's own name. |
| /// This can be overridden using `serialize="DifferentName"` or `to_string="DifferentName"` |
| /// on the attribute as shown below. |
| /// Multiple deserializations can be added to the same variant. If the variant contains additional data, |
| /// they will be set to their default values upon deserialization. |
| /// |
| /// The `default` attribute can be applied to a tuple variant with a single data parameter. When a match isn't |
| /// found, the given variant will be returned and the input string will be captured in the parameter. |
| /// |
| /// Note that the implementation of `FromStr` by default only matches on the name of the |
| /// variant. There is an option to match on different case conversions through the |
| /// `#[strum(serialize_all = "snake_case")]` type attribute. |
| /// |
| /// See the [Additional Attributes](https://docs.rs/strum/latest/strum/additional_attributes/index.html) |
| /// Section for more information on using this feature. |
| /// |
| /// If you have a large enum, you may want to consider using the `use_phf` attribute here. It leverages |
| /// perfect hash functions to parse much quicker than a standard `match`. (MSRV 1.46) |
| /// |
| /// # Example howto use `EnumString` |
| /// ``` |
| /// use std::str::FromStr; |
| /// use strum_macros::EnumString; |
| /// |
| /// #[derive(Debug, PartialEq, EnumString)] |
| /// enum Color { |
| /// Red, |
| /// // The Default value will be inserted into range if we match "Green". |
| /// Green { |
| /// range: usize, |
| /// }, |
| /// |
| /// // We can match on multiple different patterns. |
| /// #[strum(serialize = "blue", serialize = "b")] |
| /// Blue(usize), |
| /// |
| /// // Notice that we can disable certain variants from being found |
| /// #[strum(disabled)] |
| /// Yellow, |
| /// |
| /// // We can make the comparison case insensitive (however Unicode is not supported at the moment) |
| /// #[strum(ascii_case_insensitive)] |
| /// Black, |
| /// } |
| /// |
| /// /* |
| /// //The generated code will look like: |
| /// impl std::str::FromStr for Color { |
| /// type Err = ::strum::ParseError; |
| /// |
| /// fn from_str(s: &str) -> ::core::result::Result<Color, Self::Err> { |
| /// match s { |
| /// "Red" => ::core::result::Result::Ok(Color::Red), |
| /// "Green" => ::core::result::Result::Ok(Color::Green { range:Default::default() }), |
| /// "blue" => ::core::result::Result::Ok(Color::Blue(Default::default())), |
| /// "b" => ::core::result::Result::Ok(Color::Blue(Default::default())), |
| /// s if s.eq_ignore_ascii_case("Black") => ::core::result::Result::Ok(Color::Black), |
| /// _ => ::core::result::Result::Err(::strum::ParseError::VariantNotFound), |
| /// } |
| /// } |
| /// } |
| /// */ |
| /// |
| /// // simple from string |
| /// let color_variant = Color::from_str("Red").unwrap(); |
| /// assert_eq!(Color::Red, color_variant); |
| /// // short version works too |
| /// let color_variant = Color::from_str("b").unwrap(); |
| /// assert_eq!(Color::Blue(0), color_variant); |
| /// // was disabled for parsing = returns parse-error |
| /// let color_variant = Color::from_str("Yellow"); |
| /// assert!(color_variant.is_err()); |
| /// // however the variant is still normally usable |
| /// println!("{:?}", Color::Yellow); |
| /// let color_variant = Color::from_str("bLACk").unwrap(); |
| /// assert_eq!(Color::Black, color_variant); |
| /// ``` |
| #[proc_macro_derive(EnumString, attributes(strum))] |
| pub fn from_string(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| let ast = syn::parse_macro_input!(input as DeriveInput); |
| |
| let toks = |
| macros::from_string::from_string_inner(&ast).unwrap_or_else(|err| err.to_compile_error()); |
| debug_print_generated(&ast, &toks); |
| toks.into() |
| } |
| |
| /// Converts enum variants to `&'a str`, where `'a` is the lifetime of the input enum reference. |
| /// |
| /// Implements `AsRef<str>` on your enum using the same rules as |
| /// `Display` for determining what string is returned. The difference is that `as_ref()` returns |
| /// a `&str` instead of a `String` so you don't allocate any additional memory with each call. |
| /// |
| /// If you require a `&'static str`, you can use |
| /// [`strum::IntoStaticStr`](crate::IntoStaticStr) instead. |
| /// |
| /// ``` |
| /// // You need to bring the AsRef trait into scope to use it |
| /// use std::convert::AsRef; |
| /// use strum_macros::AsRefStr; |
| /// |
| /// #[derive(AsRefStr, Debug)] |
| /// enum Color { |
| /// #[strum(serialize = "redred")] |
| /// Red, |
| /// Green { |
| /// range: usize, |
| /// }, |
| /// Blue(usize), |
| /// Yellow, |
| /// } |
| /// |
| /// // uses the serialize string for Display |
| /// let red = Color::Red; |
| /// assert_eq!("redred", red.as_ref()); |
| /// // by default the variants Name |
| /// let yellow = Color::Yellow; |
| /// assert_eq!("Yellow", yellow.as_ref()); |
| /// // or for string formatting |
| /// println!( |
| /// "blue: {} green: {}", |
| /// Color::Blue(10).as_ref(), |
| /// Color::Green { range: 42 }.as_ref() |
| /// ); |
| /// |
| /// // With prefix on all variants |
| /// #[derive(AsRefStr, Debug)] |
| /// #[strum(prefix = "/")] |
| /// enum ColorWithPrefix { |
| /// #[strum(serialize = "redred")] |
| /// Red, |
| /// Green, |
| /// } |
| /// |
| /// assert_eq!("/redred", ColorWithPrefix::Red.as_ref()); |
| /// assert_eq!("/Green", ColorWithPrefix::Green.as_ref()); |
| /// ``` |
| #[proc_macro_derive(AsRefStr, attributes(strum))] |
| pub fn as_ref_str(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| let ast = syn::parse_macro_input!(input as DeriveInput); |
| |
| let toks = |
| macros::as_ref_str::as_ref_str_inner(&ast).unwrap_or_else(|err| err.to_compile_error()); |
| debug_print_generated(&ast, &toks); |
| toks.into() |
| } |
| |
| /// Implements `Strum::VariantNames` which adds an associated constant `VARIANTS` which is a `'static` slice of discriminant names. |
| /// |
| /// Adds an `impl` block for the `enum` that adds a static `VARIANTS` array of `&'static str` that are the discriminant names. |
| /// This will respect the `serialize_all` attribute on the `enum` (like `#[strum(serialize_all = "snake_case")]`. |
| /// |
| /// ``` |
| /// // import the macros needed |
| /// use strum_macros::{EnumString}; |
| /// // You need to import the trait, to have access to VARIANTS |
| /// use strum::VariantNames; |
| /// |
| /// #[derive(Debug, EnumString, strum_macros::VariantNames)] |
| /// #[strum(serialize_all = "kebab-case")] |
| /// enum Color { |
| /// Red, |
| /// Blue, |
| /// Yellow, |
| /// RebeccaPurple, |
| /// } |
| /// assert_eq!(["red", "blue", "yellow", "rebecca-purple"], Color::VARIANTS); |
| /// ``` |
| #[proc_macro_derive(VariantNames, attributes(strum))] |
| pub fn variant_names(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| let ast = syn::parse_macro_input!(input as DeriveInput); |
| |
| let toks = macros::enum_variant_names::enum_variant_names_inner(&ast) |
| .unwrap_or_else(|err| err.to_compile_error()); |
| debug_print_generated(&ast, &toks); |
| toks.into() |
| } |
| |
| #[doc(hidden)] |
| #[proc_macro_derive(EnumVariantNames, attributes(strum))] |
| #[deprecated( |
| since = "0.26.0", |
| note = "please use `#[derive(VariantNames)]` instead" |
| )] |
| pub fn variant_names_deprecated(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| let ast = syn::parse_macro_input!(input as DeriveInput); |
| |
| let toks = macros::enum_variant_names::enum_variant_names_inner(&ast) |
| .unwrap_or_else(|err| err.to_compile_error()); |
| debug_print_generated(&ast, &toks); |
| toks.into() |
| } |
| |
| /// Adds a `'static` slice with all of the Enum's variants. |
| /// |
| /// Implements `strum::VariantArray` which adds an associated constant `VARIANTS`. |
| /// This constant contains an array with all the variants of the enumerator. |
| /// |
| /// This trait can only be autoderived if the enumerator is composed only of unit-type variants, |
| /// meaning that the variants must not have any data. |
| /// |
| /// ``` |
| /// use strum::VariantArray; |
| /// |
| /// #[derive(VariantArray, Debug, PartialEq, Eq)] |
| /// enum Op { |
| /// Add, |
| /// Sub, |
| /// Mul, |
| /// Div, |
| /// } |
| /// |
| /// assert_eq!(Op::VARIANTS, &[Op::Add, Op::Sub, Op::Mul, Op::Div]); |
| /// ``` |
| #[proc_macro_derive(VariantArray, attributes(strum))] |
| pub fn static_variants_array(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| let ast = syn::parse_macro_input!(input as DeriveInput); |
| |
| let toks = macros::enum_variant_array::static_variants_array_inner(&ast) |
| .unwrap_or_else(|err| err.to_compile_error()); |
| debug_print_generated(&ast, &toks); |
| toks.into() |
| } |
| |
| #[proc_macro_derive(AsStaticStr, attributes(strum))] |
| #[doc(hidden)] |
| #[deprecated( |
| since = "0.22.0", |
| note = "please use `#[derive(IntoStaticStr)]` instead" |
| )] |
| pub fn as_static_str(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| let ast = syn::parse_macro_input!(input as DeriveInput); |
| |
| let toks = macros::as_ref_str::as_static_str_inner( |
| &ast, |
| ¯os::as_ref_str::GenerateTraitVariant::AsStaticStr, |
| ) |
| .unwrap_or_else(|err| err.to_compile_error()); |
| debug_print_generated(&ast, &toks); |
| toks.into() |
| } |
| |
| /// Implements `From<MyEnum> for &'static str` on an enum. |
| /// |
| /// Implements `From<YourEnum>` and `From<&'a YourEnum>` for `&'static str`. This is |
| /// useful for turning an enum variant into a static string. |
| /// The Rust `std` provides a blanket impl of the reverse direction - i.e. `impl Into<&'static str> for YourEnum`. |
| /// |
| /// ``` |
| /// use strum_macros::IntoStaticStr; |
| /// |
| /// #[derive(IntoStaticStr)] |
| /// enum State<'a> { |
| /// Initial(&'a str), |
| /// Finished, |
| /// } |
| /// |
| /// fn verify_state<'a>(s: &'a str) { |
| /// let mut state = State::Initial(s); |
| /// // The following won't work because the lifetime is incorrect: |
| /// // let wrong: &'static str = state.as_ref(); |
| /// // using the trait implemented by the derive works however: |
| /// let right: &'static str = state.into(); |
| /// assert_eq!("Initial", right); |
| /// state = State::Finished; |
| /// let done: &'static str = state.into(); |
| /// assert_eq!("Finished", done); |
| /// } |
| /// |
| /// verify_state(&"hello world".to_string()); |
| /// ``` |
| #[proc_macro_derive(IntoStaticStr, attributes(strum))] |
| pub fn into_static_str(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| let ast = syn::parse_macro_input!(input as DeriveInput); |
| |
| let toks = macros::as_ref_str::as_static_str_inner( |
| &ast, |
| ¯os::as_ref_str::GenerateTraitVariant::From, |
| ) |
| .unwrap_or_else(|err| err.to_compile_error()); |
| debug_print_generated(&ast, &toks); |
| toks.into() |
| } |
| |
| /// implements `std::string::ToString` on an enum |
| /// |
| /// ``` |
| /// // You need to bring the ToString trait into scope to use it |
| /// use std::string::ToString; |
| /// use strum_macros; |
| /// |
| /// #[derive(strum_macros::ToString, Debug)] |
| /// enum Color { |
| /// #[strum(serialize = "redred")] |
| /// Red, |
| /// Green { |
| /// range: usize, |
| /// }, |
| /// Blue(usize), |
| /// Yellow, |
| /// } |
| /// |
| /// // uses the serialize string for Display |
| /// let red = Color::Red; |
| /// assert_eq!(String::from("redred"), red.to_string()); |
| /// // by default the variants Name |
| /// let yellow = Color::Yellow; |
| /// assert_eq!(String::from("Yellow"), yellow.to_string()); |
| /// ``` |
| #[deprecated( |
| since = "0.22.0", |
| note = "please use `#[derive(Display)]` instead. See issue https://github.com/Peternator7/strum/issues/132" |
| )] |
| #[doc(hidden)] |
| #[proc_macro_derive(ToString, attributes(strum))] |
| pub fn to_string(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| let ast = syn::parse_macro_input!(input as DeriveInput); |
| |
| let toks = |
| macros::to_string::to_string_inner(&ast).unwrap_or_else(|err| err.to_compile_error()); |
| debug_print_generated(&ast, &toks); |
| toks.into() |
| } |
| |
| /// Converts enum variants to strings. |
| /// |
| /// Deriving `Display` on an enum prints out the given enum. This enables you to perform round |
| /// trip style conversions from enum into string and back again for unit style variants. `Display` |
| /// choose which serialization to used based on the following criteria: |
| /// |
| /// 1. If there is a `to_string` property, this value will be used. There can only be one per variant. |
| /// 2. Of the various `serialize` properties, the value with the longest length is chosen. If that |
| /// behavior isn't desired, you should use `to_string`. |
| /// 3. The name of the variant will be used if there are no `serialize` or `to_string` attributes. |
| /// 4. If the enum has a `strum(prefix = "some_value_")`, every variant will have that prefix prepended |
| /// to the serialization. |
| /// 5. Enums with named fields support named field interpolation. The value will be interpolated into the output string. |
| /// Note this means the variant will not "round trip" if you then deserialize the string. |
| /// |
| /// ```rust |
| /// #[derive(strum_macros::Display)] |
| /// pub enum Color { |
| /// #[strum(to_string = "saturation is {sat}")] |
| /// Red { sat: usize }, |
| /// } |
| /// ``` |
| /// |
| /// ``` |
| /// // You need to bring the ToString trait into scope to use it |
| /// use std::string::ToString; |
| /// use strum_macros::Display; |
| /// |
| /// #[derive(Display, Debug)] |
| /// enum Color { |
| /// #[strum(serialize = "redred")] |
| /// Red, |
| /// Green { |
| /// range: usize, |
| /// }, |
| /// Blue(usize), |
| /// Yellow, |
| /// #[strum(to_string = "purple with {sat} saturation")] |
| /// Purple { |
| /// sat: usize, |
| /// }, |
| /// } |
| /// |
| /// // uses the serialize string for Display |
| /// let red = Color::Red; |
| /// assert_eq!(String::from("redred"), format!("{}", red)); |
| /// // by default the variants Name |
| /// let yellow = Color::Yellow; |
| /// assert_eq!(String::from("Yellow"), yellow.to_string()); |
| /// // or for string formatting |
| /// println!( |
| /// "blue: {} green: {}", |
| /// Color::Blue(10), |
| /// Color::Green { range: 42 } |
| /// ); |
| /// // you can also use named fields in message |
| /// let purple = Color::Purple { sat: 10 }; |
| /// assert_eq!(String::from("purple with 10 saturation"), purple.to_string()); |
| /// ``` |
| #[proc_macro_derive(Display, attributes(strum))] |
| pub fn display(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| let ast = syn::parse_macro_input!(input as DeriveInput); |
| |
| let toks = macros::display::display_inner(&ast).unwrap_or_else(|err| err.to_compile_error()); |
| debug_print_generated(&ast, &toks); |
| toks.into() |
| } |
| |
| /// Creates a new type that iterates of the variants of an enum. |
| /// |
| /// Iterate over the variants of an Enum. Any additional data on your variants will be set to `Default::default()`. |
| /// The macro implements `strum::IntoEnumIterator` on your enum and creates a new type called `YourEnumIter` that is the iterator object. |
| /// You cannot derive `EnumIter` on any type with a lifetime bound (`<'a>`) because the iterator would surely |
| /// create [unbounded lifetimes](https://doc.rust-lang.org/nightly/nomicon/unbounded-lifetimes.html). |
| /// |
| /// ``` |
| /// |
| /// // You need to bring the trait into scope to use it! |
| /// use strum::IntoEnumIterator; |
| /// use strum_macros::EnumIter; |
| /// |
| /// #[derive(EnumIter, Debug, PartialEq)] |
| /// enum Color { |
| /// Red, |
| /// Green { range: usize }, |
| /// Blue(usize), |
| /// Yellow, |
| /// } |
| /// |
| /// // It's simple to iterate over the variants of an enum. |
| /// for color in Color::iter() { |
| /// println!("My favorite color is {:?}", color); |
| /// } |
| /// |
| /// let mut ci = Color::iter(); |
| /// assert_eq!(Some(Color::Red), ci.next()); |
| /// assert_eq!(Some(Color::Green {range: 0}), ci.next()); |
| /// assert_eq!(Some(Color::Blue(0)), ci.next()); |
| /// assert_eq!(Some(Color::Yellow), ci.next()); |
| /// assert_eq!(None, ci.next()); |
| /// ``` |
| #[proc_macro_derive(EnumIter, attributes(strum))] |
| pub fn enum_iter(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| let ast = syn::parse_macro_input!(input as DeriveInput); |
| |
| let toks = |
| macros::enum_iter::enum_iter_inner(&ast).unwrap_or_else(|err| err.to_compile_error()); |
| debug_print_generated(&ast, &toks); |
| toks.into() |
| } |
| |
| /// Generated `is_*()` methods for each variant. |
| /// E.g. `Color.is_red()`. |
| /// |
| /// ``` |
| /// |
| /// use strum_macros::EnumIs; |
| /// |
| /// #[derive(EnumIs, Debug)] |
| /// enum Color { |
| /// Red, |
| /// Green { range: usize }, |
| /// } |
| /// |
| /// assert!(Color::Red.is_red()); |
| /// assert!(Color::Green{range: 0}.is_green()); |
| /// ``` |
| #[proc_macro_derive(EnumIs, attributes(strum))] |
| pub fn enum_is(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| let ast = syn::parse_macro_input!(input as DeriveInput); |
| |
| let toks = macros::enum_is::enum_is_inner(&ast).unwrap_or_else(|err| err.to_compile_error()); |
| debug_print_generated(&ast, &toks); |
| toks.into() |
| } |
| |
| /// Generated `try_as_*()` methods for all tuple-style variants. |
| /// E.g. `Message.try_as_write()`. |
| /// |
| /// These methods will only be generated for tuple-style variants, not for named or unit variants. |
| /// |
| /// ``` |
| /// use strum_macros::EnumTryAs; |
| /// |
| /// #[derive(EnumTryAs, Debug)] |
| /// enum Message { |
| /// Quit, |
| /// Move { x: i32, y: i32 }, |
| /// Write(String), |
| /// ChangeColor(i32, i32, i32), |
| /// } |
| /// |
| /// assert_eq!( |
| /// Message::Write(String::from("Hello")).try_as_write(), |
| /// Some(String::from("Hello")) |
| /// ); |
| /// assert_eq!( |
| /// Message::ChangeColor(1, 2, 3).try_as_change_color(), |
| /// Some((1, 2, 3)) |
| /// ); |
| /// ``` |
| #[proc_macro_derive(EnumTryAs, attributes(strum))] |
| pub fn enum_try_as(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| let ast = syn::parse_macro_input!(input as DeriveInput); |
| |
| let toks = |
| macros::enum_try_as::enum_try_as_inner(&ast).unwrap_or_else(|err| err.to_compile_error()); |
| debug_print_generated(&ast, &toks); |
| toks.into() |
| } |
| |
| /// Creates a new type that maps all the variants of an enum to another generic value. |
| /// |
| /// This macro only supports enums with unit type variants.A new type called `YourEnumTable<T>`. Essentially, it's a wrapper |
| /// `[T; YourEnum::Count]` where gets/sets are infallible. Some important caveats to note: |
| /// |
| /// * The size of `YourEnumTable<T>` increases with the number of variants, not the number of values because it's always |
| /// fully populated. This means it may not be a good choice for sparsely populated maps. |
| /// |
| /// * Lookups are basically constant time since it's functionally an array index. |
| /// |
| /// * Your variants cannot have associated data. You can use `EnumDiscriminants` to generate an Enum with the same |
| /// names to work around this. |
| /// |
| /// # Stability |
| /// |
| /// Several people expressed interest in a data structure like this and pushed the PR through to completion, but the api |
| /// seems incomplete, and I reserve the right to deprecate it in the future if it becomes clear the design is flawed. |
| /// |
| /// # Example |
| /// ```rust |
| /// use strum_macros::EnumTable; |
| /// |
| /// #[derive(EnumTable)] |
| /// enum Color { |
| /// Red, |
| /// Yellow, |
| /// Green, |
| /// Blue, |
| /// } |
| /// |
| /// assert_eq!(ColorTable::default(), ColorTable::new(0, 0, 0, 0)); |
| /// assert_eq!(ColorTable::filled(2), ColorTable::new(2, 2, 2, 2)); |
| /// assert_eq!(ColorTable::from_closure(|_| 3), ColorTable::new(3, 3, 3, 3)); |
| /// assert_eq!(ColorTable::default().transform(|_, val| val + 2), ColorTable::new(2, 2, 2, 2)); |
| /// |
| /// let mut complex_map = ColorTable::from_closure(|color| match color { |
| /// Color::Red => 0, |
| /// _ => 3 |
| /// }); |
| /// |
| /// complex_map[Color::Green] = complex_map[Color::Red]; |
| /// assert_eq!(complex_map, ColorTable::new(0, 3, 0, 3)); |
| /// ``` |
| #[doc(hidden)] |
| #[proc_macro_derive(EnumTable, attributes(strum))] |
| pub fn enum_table(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| let ast = syn::parse_macro_input!(input as DeriveInput); |
| |
| let toks = |
| macros::enum_table::enum_table_inner(&ast).unwrap_or_else(|err| err.to_compile_error()); |
| debug_print_generated(&ast, &toks); |
| toks.into() |
| } |
| |
| /// Add a function to enum that allows accessing variants by its discriminant |
| /// |
| /// This macro adds a standalone function to obtain an enum variant by its discriminant. The macro adds |
| /// `from_repr(discriminant: usize) -> Option<YourEnum>` as a standalone function on the enum. For |
| /// variants with additional data, the returned variant will use the `Default` trait to fill the |
| /// data. The discriminant follows the same rules as `rustc`. The first discriminant is zero and each |
| /// successive variant has a discriminant of one greater than the previous variant, except where an |
| /// explicit discriminant is specified. The type of the discriminant will match the `repr` type if |
| /// it is specifed. |
| /// |
| /// When the macro is applied using rustc >= 1.46 and when there is no additional data on any of |
| /// the variants, the `from_repr` function is marked `const`. rustc >= 1.46 is required |
| /// to allow `match` statements in `const fn`. The no additional data requirement is due to the |
| /// inability to use `Default::default()` in a `const fn`. |
| /// |
| /// You cannot derive `FromRepr` on any type with a lifetime bound (`<'a>`) because the function would surely |
| /// create [unbounded lifetimes](https://doc.rust-lang.org/nightly/nomicon/unbounded-lifetimes.html). |
| /// |
| /// ``` |
| /// |
| /// use strum_macros::FromRepr; |
| /// |
| /// #[derive(FromRepr, Debug, PartialEq)] |
| /// enum Color { |
| /// Red, |
| /// Green { range: usize }, |
| /// Blue(usize), |
| /// Yellow, |
| /// } |
| /// |
| /// assert_eq!(Some(Color::Red), Color::from_repr(0)); |
| /// assert_eq!(Some(Color::Green {range: 0}), Color::from_repr(1)); |
| /// assert_eq!(Some(Color::Blue(0)), Color::from_repr(2)); |
| /// assert_eq!(Some(Color::Yellow), Color::from_repr(3)); |
| /// assert_eq!(None, Color::from_repr(4)); |
| /// |
| /// // Custom discriminant tests |
| /// #[derive(FromRepr, Debug, PartialEq)] |
| /// #[repr(u8)] |
| /// enum Vehicle { |
| /// Car = 1, |
| /// Truck = 3, |
| /// } |
| /// |
| /// assert_eq!(None, Vehicle::from_repr(0)); |
| /// ``` |
| /// |
| /// On versions of rust >= 1.46, the `from_repr` function is marked `const`. |
| /// |
| /// ```rust |
| /// use strum_macros::FromRepr; |
| /// |
| /// #[derive(FromRepr, Debug, PartialEq)] |
| /// #[repr(u8)] |
| /// enum Number { |
| /// One = 1, |
| /// Three = 3, |
| /// } |
| /// |
| /// # #[rustversion::since(1.46)] |
| /// const fn number_from_repr(d: u8) -> Option<Number> { |
| /// Number::from_repr(d) |
| /// } |
| /// |
| /// # #[rustversion::before(1.46)] |
| /// # fn number_from_repr(d: u8) -> Option<Number> { |
| /// # Number::from_repr(d) |
| /// # } |
| /// assert_eq!(None, number_from_repr(0)); |
| /// assert_eq!(Some(Number::One), number_from_repr(1)); |
| /// assert_eq!(None, number_from_repr(2)); |
| /// assert_eq!(Some(Number::Three), number_from_repr(3)); |
| /// assert_eq!(None, number_from_repr(4)); |
| /// ``` |
| #[proc_macro_derive(FromRepr, attributes(strum))] |
| pub fn from_repr(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| let ast = syn::parse_macro_input!(input as DeriveInput); |
| |
| let toks = |
| macros::from_repr::from_repr_inner(&ast).unwrap_or_else(|err| err.to_compile_error()); |
| debug_print_generated(&ast, &toks); |
| toks.into() |
| } |
| |
| /// Add a verbose message to an enum variant. |
| /// |
| /// Encode strings into the enum itself. The `strum_macros::EmumMessage` macro implements the `strum::EnumMessage` trait. |
| /// `EnumMessage` looks for `#[strum(message="...")]` attributes on your variants. |
| /// You can also provided a `detailed_message="..."` attribute to create a seperate more detailed message than the first. |
| /// |
| /// `EnumMessage` also exposes the variants doc comments through `get_documentation()`. This is useful in some scenarios, |
| /// but `get_message` should generally be preferred. Rust doc comments are intended for developer facing documentation, |
| /// not end user messaging. |
| /// |
| /// ``` |
| /// // You need to bring the trait into scope to use it |
| /// use strum::EnumMessage; |
| /// use strum_macros; |
| /// |
| /// #[derive(strum_macros::EnumMessage, Debug)] |
| /// #[allow(dead_code)] |
| /// enum Color { |
| /// /// Danger color. |
| /// #[strum(message = "Red", detailed_message = "This is very red")] |
| /// Red, |
| /// #[strum(message = "Simply Green")] |
| /// Green { range: usize }, |
| /// #[strum(serialize = "b", serialize = "blue")] |
| /// Blue(usize), |
| /// } |
| /// |
| /// // Generated code looks like more or less like this: |
| /// /* |
| /// impl ::strum::EnumMessage for Color { |
| /// fn get_message(&self) -> ::core::option::Option<&'static str> { |
| /// match self { |
| /// &Color::Red => ::core::option::Option::Some("Red"), |
| /// &Color::Green {..} => ::core::option::Option::Some("Simply Green"), |
| /// _ => None |
| /// } |
| /// } |
| /// |
| /// fn get_detailed_message(&self) -> ::core::option::Option<&'static str> { |
| /// match self { |
| /// &Color::Red => ::core::option::Option::Some("This is very red"), |
| /// &Color::Green {..}=> ::core::option::Option::Some("Simply Green"), |
| /// _ => None |
| /// } |
| /// } |
| /// |
| /// fn get_documentation(&self) -> ::std::option::Option<&'static str> { |
| /// match self { |
| /// &Color::Red => ::std::option::Option::Some("Danger color."), |
| /// _ => None |
| /// } |
| /// } |
| /// |
| /// fn get_serializations(&self) -> &'static [&'static str] { |
| /// match self { |
| /// &Color::Red => { |
| /// static ARR: [&'static str; 1] = ["Red"]; |
| /// &ARR |
| /// }, |
| /// &Color::Green {..}=> { |
| /// static ARR: [&'static str; 1] = ["Green"]; |
| /// &ARR |
| /// }, |
| /// &Color::Blue (..) => { |
| /// static ARR: [&'static str; 2] = ["b", "blue"]; |
| /// &ARR |
| /// }, |
| /// } |
| /// } |
| /// } |
| /// */ |
| /// |
| /// let c = Color::Red; |
| /// assert_eq!("Red", c.get_message().unwrap()); |
| /// assert_eq!("This is very red", c.get_detailed_message().unwrap()); |
| /// assert_eq!("Danger color.", c.get_documentation().unwrap()); |
| /// assert_eq!(["Red"], c.get_serializations()); |
| /// ``` |
| #[proc_macro_derive(EnumMessage, attributes(strum))] |
| pub fn enum_messages(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| let ast = syn::parse_macro_input!(input as DeriveInput); |
| |
| let toks = macros::enum_messages::enum_message_inner(&ast) |
| .unwrap_or_else(|err| err.to_compile_error()); |
| debug_print_generated(&ast, &toks); |
| toks.into() |
| } |
| |
| /// Add custom properties to enum variants. |
| /// |
| /// Enables the encoding of arbitary constants into enum variants. This method |
| /// currently only supports adding additional string values. Other types of literals are still |
| /// experimental in the rustc compiler. The generated code works by nesting match statements. |
| /// The first match statement matches on the type of the enum, and the inner match statement |
| /// matches on the name of the property requested. This design works well for enums with a small |
| /// number of variants and properties, but scales linearly with the number of variants so may not |
| /// be the best choice in all situations. |
| /// |
| /// ``` |
| /// |
| /// use strum_macros; |
| /// // bring the trait into scope |
| /// use strum::EnumProperty; |
| /// |
| /// #[derive(strum_macros::EnumProperty, Debug)] |
| /// #[allow(dead_code)] |
| /// enum Color { |
| /// #[strum(props(Red = "255", Blue = "255", Green = "255"))] |
| /// White, |
| /// #[strum(props(Red = "0", Blue = "0", Green = "0"))] |
| /// Black, |
| /// #[strum(props(Red = "0", Blue = "255", Green = "0"))] |
| /// Blue, |
| /// #[strum(props(Red = "255", Blue = "0", Green = "0"))] |
| /// Red, |
| /// #[strum(props(Red = "0", Blue = "0", Green = "255"))] |
| /// Green, |
| /// } |
| /// |
| /// let my_color = Color::Red; |
| /// let display = format!( |
| /// "My color is {:?}. It's RGB is {},{},{}", |
| /// my_color, |
| /// my_color.get_str("Red").unwrap(), |
| /// my_color.get_str("Green").unwrap(), |
| /// my_color.get_str("Blue").unwrap() |
| /// ); |
| /// assert_eq!("My color is Red. It\'s RGB is 255,0,0", &display); |
| /// ``` |
| |
| #[proc_macro_derive(EnumProperty, attributes(strum))] |
| pub fn enum_properties(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| let ast = syn::parse_macro_input!(input as DeriveInput); |
| |
| let toks = macros::enum_properties::enum_properties_inner(&ast) |
| .unwrap_or_else(|err| err.to_compile_error()); |
| debug_print_generated(&ast, &toks); |
| toks.into() |
| } |
| |
| /// Generate a new type with only the discriminant names. |
| /// |
| /// Given an enum named `MyEnum`, generates another enum called `MyEnumDiscriminants` with the same |
| /// variants but without any data fields. This is useful when you wish to determine the variant of |
| /// an `enum` but one or more of the variants contains a non-`Default` field. `From` |
| /// implementations are generated so that you can easily convert from `MyEnum` to |
| /// `MyEnumDiscriminants`. |
| /// |
| /// By default, the generated enum has the following derives: `Clone, Copy, Debug, PartialEq, Eq`. |
| /// You can add additional derives using the `#[strum_discriminants(derive(AdditionalDerive))]` |
| /// attribute. |
| /// |
| /// Note, the variant attributes passed to the discriminant enum are filtered to avoid compilation |
| /// errors due to the derives mismatches, thus only `#[doc]`, `#[cfg]`, `#[allow]`, and `#[deny]` |
| /// are passed through by default. If you want to specify a custom attribute on the discriminant |
| /// variant, wrap it with `#[strum_discriminants(...)]` attribute. |
| /// |
| /// ``` |
| /// // Bring trait into scope |
| /// use std::str::FromStr; |
| /// use strum::{IntoEnumIterator, EnumMessage}; |
| /// use strum_macros::{EnumDiscriminants, EnumIter, EnumString}; |
| /// |
| /// #[derive(Debug)] |
| /// struct NonDefault; |
| /// |
| /// // simple example |
| /// # #[allow(dead_code)] |
| /// #[derive(Debug, EnumDiscriminants)] |
| /// #[strum_discriminants(derive(EnumString, EnumMessage))] |
| /// enum MyEnum { |
| /// #[strum_discriminants(strum(message = "Variant zero"))] |
| /// Variant0(NonDefault), |
| /// Variant1 { a: NonDefault }, |
| /// } |
| /// |
| /// // You can rename the generated enum using the `#[strum_discriminants(name(OtherName))]` attribute: |
| /// # #[allow(dead_code)] |
| /// #[derive(Debug, EnumDiscriminants)] |
| /// #[strum_discriminants(derive(EnumIter))] |
| /// #[strum_discriminants(name(MyVariants))] |
| /// enum MyEnumR { |
| /// Variant0(bool), |
| /// Variant1 { a: bool }, |
| /// } |
| /// |
| /// // test simple example |
| /// assert_eq!( |
| /// MyEnumDiscriminants::Variant0, |
| /// MyEnumDiscriminants::from_str("Variant0").unwrap() |
| /// ); |
| /// // test rename example combined with EnumIter |
| /// assert_eq!( |
| /// vec![MyVariants::Variant0, MyVariants::Variant1], |
| /// MyVariants::iter().collect::<Vec<_>>() |
| /// ); |
| /// |
| /// // Make use of the auto-From conversion to check whether an instance of `MyEnum` matches a |
| /// // `MyEnumDiscriminants` discriminant. |
| /// assert_eq!( |
| /// MyEnumDiscriminants::Variant0, |
| /// MyEnum::Variant0(NonDefault).into() |
| /// ); |
| /// assert_eq!( |
| /// MyEnumDiscriminants::Variant0, |
| /// MyEnumDiscriminants::from(MyEnum::Variant0(NonDefault)) |
| /// ); |
| /// |
| /// // Make use of the EnumMessage on the `MyEnumDiscriminants` discriminant. |
| /// assert_eq!( |
| /// MyEnumDiscriminants::Variant0.get_message(), |
| /// Some("Variant zero") |
| /// ); |
| /// ``` |
| /// |
| /// It is also possible to specify the visibility (e.g. `pub`/`pub(crate)`/etc.) |
| /// of the generated enum. By default, the generated enum inherits the |
| /// visibility of the parent enum it was generated from. |
| /// |
| /// ``` |
| /// use strum_macros::EnumDiscriminants; |
| /// |
| /// // You can set the visibility of the generated enum using the `#[strum_discriminants(vis(..))]` attribute: |
| /// mod inner { |
| /// use strum_macros::EnumDiscriminants; |
| /// |
| /// # #[allow(dead_code)] |
| /// #[derive(Debug, EnumDiscriminants)] |
| /// #[strum_discriminants(vis(pub))] |
| /// #[strum_discriminants(name(PubDiscriminants))] |
| /// enum PrivateEnum { |
| /// Variant0(bool), |
| /// Variant1 { a: bool }, |
| /// } |
| /// } |
| /// |
| /// // test visibility example, `PrivateEnum` should not be accessible here |
| /// assert_ne!( |
| /// inner::PubDiscriminants::Variant0, |
| /// inner::PubDiscriminants::Variant1, |
| /// ); |
| /// ``` |
| #[proc_macro_derive(EnumDiscriminants, attributes(strum, strum_discriminants))] |
| pub fn enum_discriminants(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| let ast = syn::parse_macro_input!(input as DeriveInput); |
| |
| let toks = macros::enum_discriminants::enum_discriminants_inner(&ast) |
| .unwrap_or_else(|err| err.to_compile_error()); |
| debug_print_generated(&ast, &toks); |
| toks.into() |
| } |
| |
| /// Add a constant `usize` equal to the number of variants. |
| /// |
| /// For a given enum generates implementation of `strum::EnumCount`, |
| /// which adds a static property `COUNT` of type usize that holds the number of variants. |
| /// |
| /// ``` |
| /// use strum::{EnumCount, IntoEnumIterator}; |
| /// use strum_macros::{EnumCount as EnumCountMacro, EnumIter}; |
| /// |
| /// #[derive(Debug, EnumCountMacro, EnumIter)] |
| /// enum Week { |
| /// Sunday, |
| /// Monday, |
| /// Tuesday, |
| /// Wednesday, |
| /// Thursday, |
| /// Friday, |
| /// Saturday, |
| /// } |
| /// |
| /// assert_eq!(7, Week::COUNT); |
| /// assert_eq!(Week::iter().count(), Week::COUNT); |
| /// |
| /// ``` |
| #[proc_macro_derive(EnumCount, attributes(strum))] |
| pub fn enum_count(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| let ast = syn::parse_macro_input!(input as DeriveInput); |
| let toks = |
| macros::enum_count::enum_count_inner(&ast).unwrap_or_else(|err| err.to_compile_error()); |
| debug_print_generated(&ast, &toks); |
| toks.into() |
| } |