2017-02-12 07:46:32 +01:00
|
|
|
# Strum
|
|
|
|
|
2017-02-12 08:00:43 +01:00
|
|
|
[![Build Status](https://travis-ci.org/Peternator7/strum.svg?branch=master)](https://travis-ci.org/Peternator7/strum)
|
|
|
|
[![Latest Version](https://img.shields.io/crates/v/strum.svg)](https://crates.io/crates/strum)
|
2017-02-12 08:03:42 +01:00
|
|
|
[![Rust Documentation](https://docs.rs/strum/badge.svg)](https://docs.rs/strum)
|
2017-02-12 08:00:43 +01:00
|
|
|
|
2017-02-12 07:46:32 +01:00
|
|
|
Strum is a set of macros and traits for working with
|
|
|
|
enums and strings easier in Rust.
|
|
|
|
|
|
|
|
# Including Strum in Your Project
|
|
|
|
|
|
|
|
Import strum and strum_macros into your project by adding the following lines to your
|
|
|
|
Cargo.toml. Strum_macros contains the macros needed to derive all the traits in Strum.
|
|
|
|
|
|
|
|
```toml
|
|
|
|
[dependencies]
|
|
|
|
strum = "*"
|
|
|
|
strum_macros = "*"
|
|
|
|
```
|
|
|
|
|
|
|
|
And add these lines to the root of your project, either lib.rs or main.rs.
|
|
|
|
|
|
|
|
```rust
|
|
|
|
// Strum contains all the trait definitions
|
|
|
|
extern crate strum;
|
|
|
|
#[macro_use]
|
|
|
|
extern crate strum_macros;
|
|
|
|
# fn main() {}
|
|
|
|
```
|
|
|
|
|
|
|
|
# Strum Macros
|
|
|
|
|
|
|
|
Strum has implemented the following macros:
|
|
|
|
|
|
|
|
1. `EnumString`: 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"` 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.
|
|
|
|
|
|
|
|
Here is an example of the code generated by deriving `EnumString`.
|
|
|
|
|
|
|
|
```
|
|
|
|
# extern crate strum;
|
|
|
|
# #[macro_use] extern crate strum_macros;
|
|
|
|
#[derive(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="true")]
|
|
|
|
Yellow,
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
//The generated code will look like:
|
|
|
|
impl std::str::FromStr for Color {
|
|
|
|
type Err = strum::ParseError;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Color, strum::ParseError> {
|
|
|
|
match s {
|
|
|
|
"Red" => Ok(Color::Red),
|
|
|
|
"Green" => Ok(Color::Green { range:Default::default() }),
|
|
|
|
"blue" | "b" => Ok(Color::Blue(Default::default())),
|
|
|
|
_ => Err(strum::ParseError::VariantNotFound),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
# fn main() {}
|
|
|
|
```
|
|
|
|
|
|
|
|
Note that the implementation of `FromStr` only matches on the name of the variant.
|
|
|
|
Strum, where possible, avoids operations that have an unknown runtime cost, and parsing strings
|
|
|
|
is potentially an expensive operation. If you do need that behavior, consider the more powerful
|
|
|
|
Serde library for your serialization.
|
|
|
|
|
|
|
|
2. `EnumIter`: 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).
|
|
|
|
|
|
|
|
```rust
|
|
|
|
# extern crate strum;
|
|
|
|
# #[macro_use] extern crate strum_macros;
|
|
|
|
# use std::fmt::Debug;
|
|
|
|
// You need to bring the type into scope to use it!!!
|
|
|
|
use strum::IntoEnumIterator;
|
|
|
|
|
|
|
|
#[derive(EnumIter,Debug)]
|
|
|
|
enum Color {
|
|
|
|
Red,
|
|
|
|
Green { range:usize },
|
|
|
|
Blue(usize),
|
|
|
|
Yellow,
|
|
|
|
}
|
|
|
|
|
|
|
|
// It's simple to iterate over the variants of an enum.
|
|
|
|
fn debug_colors() {
|
|
|
|
for color in Color::iter() {
|
|
|
|
println!("My favorite color is {:?}", color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
debug_colors();
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
3. `EnumMessage`: encode strings into the enum itself. This 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.
|
|
|
|
|
|
|
|
The generated code will look something like:
|
|
|
|
|
|
|
|
```rust
|
|
|
|
# extern crate strum;
|
|
|
|
# #[macro_use] extern crate strum_macros;
|
|
|
|
// You need to bring the type into scope to use it!!!
|
|
|
|
use strum::EnumMessage;
|
|
|
|
|
|
|
|
#[derive(EnumMessage,Debug)]
|
|
|
|
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
|
|
|
|
impl EnumMessage for Color {
|
|
|
|
fn get_message(&self) -> Option<&str> {
|
|
|
|
match self {
|
|
|
|
&Color::Red => Some("Red"),
|
|
|
|
&Color::Green {..} => Some("Simply Green"),
|
|
|
|
_ => None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_detailed_message(&self) -> Option<&str> {
|
|
|
|
match self {
|
|
|
|
&Color::Red => Some("This is very red"),
|
|
|
|
&Color::Green {..}=> 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
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
# fn main() {}
|
|
|
|
```
|
|
|
|
|
|
|
|
# Additional Attributes
|
|
|
|
|
|
|
|
Strum supports several custom attributes to modify the generated code. 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.
|
|
|
|
|
|
|
|
- `default="true"`: 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.
|
|
|
|
|
|
|
|
```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="true"`: 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.
|
|
|
|
|
|
|
|
# Examples
|
|
|
|
|
|
|
|
Using `EnumMessage` for quickly implementing `Error`
|
|
|
|
|
|
|
|
```rust
|
|
|
|
extern crate strum;
|
|
|
|
#[macro_use]
|
|
|
|
extern crate strum_macros;
|
|
|
|
# use std::error::Error;
|
|
|
|
# use std::fmt::*;
|
|
|
|
use strum::EnumMessage;
|
|
|
|
|
|
|
|
#[derive(Debug, EnumMessage)]
|
|
|
|
enum ServerError {
|
|
|
|
#[strum(message="A network error occured")]
|
|
|
|
#[strum(detailed_message="Try checking your connection.")]
|
|
|
|
NetworkError,
|
|
|
|
#[strum(message="User input error.")]
|
|
|
|
#[strum(detailed_message="There was an error parsing user input. Please try again.")]
|
|
|
|
InvalidUserInputError,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for ServerError {
|
|
|
|
fn fmt(&self, f: &mut Formatter) -> Result {
|
|
|
|
write!(f, "{}", self.get_message().unwrap())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Error for ServerError {
|
|
|
|
fn description(&self) -> &str {
|
|
|
|
self.get_detailed_message().unwrap()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
# fn main() {}
|
|
|
|
```
|
|
|
|
|
|
|
|
Using `EnumString` to tokenize a series of inputs:
|
|
|
|
|
|
|
|
```rust
|
|
|
|
extern crate strum;
|
|
|
|
#[macro_use]
|
|
|
|
extern crate strum_macros;
|
|
|
|
use std::str::FromStr;
|
|
|
|
|
|
|
|
#[derive(Eq, PartialEq, Debug, EnumString)]
|
|
|
|
enum Tokens {
|
|
|
|
#[strum(serialize="fn")]
|
|
|
|
Function,
|
|
|
|
#[strum(serialize="(")]
|
|
|
|
OpenParen,
|
|
|
|
#[strum(serialize=")")]
|
|
|
|
CloseParen,
|
|
|
|
#[strum(default="true")]
|
|
|
|
Ident(String)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
let toks = ["fn", "hello_world", "(", ")"].iter()
|
|
|
|
.map(|tok| Tokens::from_str(tok).unwrap())
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
assert_eq!(toks, vec![Tokens::Function,
|
|
|
|
Tokens::Ident(String::from("hello_world")),
|
|
|
|
Tokens::OpenParen,
|
|
|
|
Tokens::CloseParen]);
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
# Name
|
|
|
|
|
|
|
|
Strum is short for STRing enUM because it's a library for augmenting enums with additional
|
|
|
|
information through strings.
|
|
|
|
|
|
|
|
Strumming is also a very whimsical motion, much like writing Rust code.
|