1
0
mirror of https://github.com/danog/strum.git synced 2024-11-26 20:14:40 +01:00

Wiki to doc tests (#121)

* example usage as doctests added to the strum_macros crate

* removed links to to wiki example code and documentation of the macros

* added attribute documentation as a module in strum and linked to it
This commit is contained in:
Michael Kefeder 2020-10-06 19:17:06 +02:00 committed by GitHub
parent 7e64a470b1
commit 89b9c14731
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 504 additions and 31 deletions

View File

@ -0,0 +1,68 @@
//! # Documentation for Additional Attributes
//!
//! Strum supports several custom attributes to modify the generated code. At the enum level, the
//! `#[strum(serialize_all = "snake_case")]` attribute can be used to change the case used when
//! serializing to and deserializing from strings:
//!
//! ```rust
//! use std::string::ToString;
//! use strum;
//! use strum_macros;
//!
//! #[derive(Debug, Eq, PartialEq, strum_macros::ToString)]
//! #[strum(serialize_all = "snake_case")]
//! enum Brightness {
//! DarkBlack,
//! Dim {
//! glow: usize,
//! },
//! #[strum(serialize = "bright")]
//! BrightWhite,
//! }
//!
//! assert_eq!(
//! String::from("dark_black"),
//! Brightness::DarkBlack.to_string().as_ref()
//! );
//! assert_eq!(
//! String::from("dim"),
//! Brightness::Dim { glow: 0 }.to_string().as_ref()
//! );
//! assert_eq!(
//! String::from("bright"),
//! Brightness::BrightWhite.to_string().as_ref()
//! );
//! ```
//!
//! Custom attributes are applied to a variant by adding `#[strum(parameter="value")]` to the variant.
//!
//! - `serialize="..."`: Changes the text that `FromStr()` looks for when parsing a string. This attribute can
//! be applied multiple times to an element and the enum variant will be parsed if any of them match.
//!
//! - `to_string="..."`: Similar to `serialize`. This value will be included when using `FromStr()`. More importantly,
//! this specifies what text to use when calling `variant.to_string()` with the `ToString` derivation, or when calling `variant.as_ref()` with `AsRefStr`.
//!
//! - `default`: Applied to a single variant of an enum. The variant must be a Tuple-like
//! variant with a single piece of data that can be create from a `&str` i.e. `T: From<&str>`.
//! The generated code will now return the variant with the input string captured as shown below
//! instead of failing.
//!
//! ```rust,ignore
//! // Replaces this:
//! _ => Err(strum::ParseError::VariantNotFound)
//! // With this in generated code:
//! default => Ok(Variant(default.into()))
//! ```
//! The plugin will fail if the data doesn't implement From<&str>. You can only have one `default`
//! on your enum.
//!
//! - `disabled`: removes variant from generated code.
//!
//! - `message=".."`: Adds a message to enum variant. This is used in conjunction with the `EnumMessage`
//! trait to associate a message with a variant. If `detailed_message` is not provided,
//! then `message` will also be returned when get_detailed_message() is called.
//!
//! - `detailed_message=".."`: Adds a more detailed message to a variant. If this value is omitted, then
//! `message` will be used in it's place.
//!
//! - `props(key="value")`: Enables associating additional information with a given variant.

View File

@ -20,33 +20,9 @@
//! strum_macros = "0.18.0"
//! ```
//!
//! # Strum Macros
//!
//! Strum has implemented the following macros:
//!
//! | Macro | Description |
//! | --- | ----------- |
//! | [EnumString] | Converts strings to enum variants based on their name |
//! | [Display] | Converts enum variants to strings |
//! | [AsRefStr] | Converts enum variants to `&'static str` |
//! | [IntoStaticStr] | Implements `From<MyEnum> for &'static str` on an enum |
//! | [EnumVariantNames] | Implements Strum::VariantNames which adds an associated constant `VARIANTS` which is an array of discriminant names |
//! | [EnumIter] | Creates a new type that iterates of the variants of an enum. |
//! | [EnumProperty] | Add custom properties to enum variants. |
//! | [EnumMessage] | Add a verbose message to an enum variant. |
//! | [EnumDiscriminants] | Generate a new type with only the discriminant names. |
//! | [EnumCount] | Add a constant `usize` equal to the number of variants. |
//!
//! [EnumString]: https://github.com/Peternator7/strum/wiki/Derive-EnumString
//! [Display]: https://github.com/Peternator7/strum/wiki/Derive-Display
//! [AsRefStr]: https://github.com/Peternator7/strum/wiki/Derive-AsRefStr
//! [IntoStaticStr]: https://github.com/Peternator7/strum/wiki/Derive-IntoStaticStr
//! [EnumVariantNames]: https://github.com/Peternator7/strum/wiki/Derive-EnumVariantNames
//! [EnumIter]: https://github.com/Peternator7/strum/wiki/Derive-EnumIter
//! [EnumProperty]: https://github.com/Peternator7/strum/wiki/Derive-EnumProperty
//! [EnumMessage]: https://github.com/Peternator7/strum/wiki/Derive-EnumMessage
//! [EnumDiscriminants]: https://github.com/Peternator7/strum/wiki/Derive-EnumDiscriminants
//! [EnumCount]: https://github.com/Peternator7/strum/wiki/Derive-EnumCount
// only for documentation purposes
pub mod additional_attributes;
/// The ParseError enum is a collection of all the possible reasons
/// an enum can fail to parse from a string.
@ -204,5 +180,4 @@ pub trait VariantNames {
}
#[cfg(feature = "derive")]
#[doc(hidden)]
pub use strum_macros::*;

View File

@ -24,6 +24,9 @@ proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "1.0", features = ["parsing", "extra-traits"] }
[dev-dependencies]
strum = "0.19"
[features]
verbose-enumstring-name = []
verbose-asrefstr-name = []

View File

@ -3,9 +3,6 @@
//! Strum is a set of macros and traits for working with
//! enums and strings easier in Rust.
//!
//! # Documentation
//!
//! The documentation for this crate is found in the `strum` crate.
#![recursion_limit = "128"]
@ -30,6 +27,74 @@ fn debug_print_generated(ast: &syn::DeriveInput, toks: &TokenStream) {
}
}
/// Converts strings to enum variants based on their name.
///
/// auto-derives `std::str::FromStr` on the enum. 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](../strum/additional_attributes/index.html)
/// Section for more information on using this feature.
///
/// # 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,
/// }
///
/// /*
/// //The generated code will look like:
/// impl std::str::FromStr for Color {
/// type Err = ::strum::ParseError;
///
/// fn from_str(s: &str) -> ::std::result::Result<Color, Self::Err> {
/// match s {
/// "Red" => ::std::result::Result::Ok(Color::Red),
/// "Green" => ::std::result::Result::Ok(Color::Green { range:Default::default() }),
/// "blue" | "b" => ::std::result::Result::Ok(Color::Blue(Default::default())),
/// _ => ::std::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);
/// ```
#[cfg_attr(
not(feature = "verbose-enumstring-name"),
proc_macro_derive(EnumString, attributes(strum))
@ -46,6 +111,43 @@ pub fn from_string(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
toks.into()
}
/// Converts enum variants to `&'static str`.
///
/// 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.
///
/// ```
/// // 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,
/// }
///
/// fn main() {
/// // 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()
/// );
/// }
/// ```
#[cfg_attr(
not(feature = "verbose-asrefstr-name"),
proc_macro_derive(AsRefStr, attributes(strum))
@ -62,6 +164,27 @@ pub fn as_ref_str(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
toks.into()
}
/// Implements Strum::VariantNames which adds an associated constant `VARIANTS` which is an array 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, EnumVariantNames};
/// // You need to import the trait, to have access to VARIANTS
/// use strum::VariantNames;
///
/// #[derive(Debug, EnumString, EnumVariantNames)]
/// #[strum(serialize_all = "kebab_case")]
/// enum Color {
/// Red,
/// Blue,
/// Yellow,
/// RebeccaPurple,
/// }
/// assert_eq!(["red", "blue", "yellow", "rebecca-purple"], Color::VARIANTS);
/// ```
#[cfg_attr(
not(feature = "verbose-variant-names"),
proc_macro_derive(EnumVariantNames, attributes(strum))
@ -97,6 +220,35 @@ pub fn as_static_str(input: proc_macro::TokenStream) -> proc_macro::TokenStream
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());
/// ```
#[cfg_attr(
feature = "verbose-intostaticstr-name",
proc_macro_derive(StrumIntoStaticStr, attributes(strum))
@ -116,6 +268,31 @@ pub fn into_static_str(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
toks.into()
}
/// implements `std::string::ToString` on en 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());
/// ```
#[cfg_attr(
feature = "verbose-tostring-name",
proc_macro_derive(StrumToString, attributes(strum))
@ -132,6 +309,46 @@ pub fn to_string(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
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.
/// 1. Of the various `serialize` properties, the value with the longest length is chosen. If that
/// behavior isn't desired, you should use `to_string`.
/// 1. The name of the variant will be used if there are no `serialize` or `to_string` attributes.
///
/// ```
/// // 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,
/// }
///
/// // 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 }
/// );
/// ```
#[cfg_attr(
feature = "verbose-display-name",
proc_macro_derive(StrumDisplay, attributes(strum))
@ -148,6 +365,39 @@ pub fn display(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
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::IntoEnumIter` 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());
/// ```
#[cfg_attr(
feature = "verbose-enumiter-name",
proc_macro_derive(StrumEnumIter, attributes(strum))
@ -164,6 +414,70 @@ pub fn enum_iter(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
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.
/// ```
/// // 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 {
/// #[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) -> ::std::option::Option<&str> {
/// match self {
/// &Color::Red => ::std::option::Option::Some("Red"),
/// &Color::Green {..} => ::std::option::Option::Some("Simply Green"),
/// _ => None
/// }
/// }
///
/// fn get_detailed_message(&self) -> ::std::option::Option<&str> {
/// match self {
/// &Color::Red => ::std::option::Option::Some("This is very red"),
/// &Color::Green {..}=> ::std::option::Option::Some("Simply Green"),
/// _ => None
/// }
/// }
///
/// fn get_serializations(&self) -> &[&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!(["Red"], c.get_serializations());
/// ```
#[cfg_attr(
feature = "verbose-enummessage-name",
proc_macro_derive(StrumEnumMessage, attributes(strum))
@ -180,6 +494,48 @@ pub fn enum_messages(input: proc_macro::TokenStream) -> proc_macro::TokenStream
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);
/// ```
#[cfg_attr(
feature = "verbose-enumproperty-name",
proc_macro_derive(StrumEnumProperty, attributes(strum))
@ -196,6 +552,53 @@ pub fn enum_properties(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
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, without any data fields.
/// This is useful when you wish to determine the variant of an enum from a String, but the variants contain any
/// non-`Default` fields. 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.
///
/// ```
/// // Bring trait into scope
/// use std::str::FromStr;
/// use strum::IntoEnumIterator;
/// use strum_macros::{EnumDiscriminants, EnumIter, EnumString};
///
/// #[derive(Debug)]
/// struct NonDefault;
///
/// // simple example
/// # #[allow(dead_code)]
/// #[derive(Debug, EnumDiscriminants)]
/// #[strum_discriminants(derive(EnumString))]
/// enum MyEnum {
/// Variant0(NonDefault),
/// Variant1 { a: NonDefault },
/// }
///
/// // You can also 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<_>>()
/// );
/// ```
#[cfg_attr(
feature = "verbose-enumdiscriminants-name",
proc_macro_derive(StrumEnumDiscriminants, attributes(strum, strum_discriminants))
@ -212,6 +615,30 @@ pub fn enum_discriminants(input: proc_macro::TokenStream) -> proc_macro::TokenSt
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);
///
/// ```
#[cfg_attr(
feature = "verbose-enumcount-name",
proc_macro_derive(StrumEnumCount, attributes(strum))