mirror of
https://github.com/danog/strum.git
synced 2024-11-26 20:14:40 +01:00
Fixed all the doc tests to actually run.
This commit is contained in:
parent
e9f28d8273
commit
bbdceacd58
@ -3,4 +3,8 @@ name = "strum"
|
||||
version = "0.1.0"
|
||||
authors = ["Peter Glotfelty <peglotfe@microsoft.com>"]
|
||||
|
||||
[dependencies]
|
||||
[dev-dependencies]
|
||||
strum_macros = { path = "../strum_macros" }
|
||||
|
||||
[lib]
|
||||
name = "strum"
|
142
strum/src/lib.rs
142
strum/src/lib.rs
@ -18,23 +18,24 @@
|
||||
//!
|
||||
//! And you need to add these to the root of your project, either lib.rs or main.rs.
|
||||
//!
|
||||
//! ```dontrun
|
||||
//! ```rust
|
||||
//! // Strum contains all the trait definitions
|
||||
//! extern crate strum;
|
||||
//! #[macro_use]
|
||||
//! extern crate strum_macros;
|
||||
//!
|
||||
//! // Import the traits we use into scope.
|
||||
//! use strum::*;
|
||||
//! use strum::{IntoEnumIterator, EnumMessage};
|
||||
//!
|
||||
//! #[derive(EnumString,EnumIter,EnumMessage)]
|
||||
//! pub enum Color {
|
||||
//! Red,
|
||||
//! #[strum(serialize="b", serialize="blue", serialize="verboseblue")]
|
||||
//! Blue,
|
||||
//! #[strum(help="This is the color yellow")]
|
||||
//! #[strum(message="This is the color yellow")]
|
||||
//! Yellow
|
||||
//! }
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! # Strum Macros
|
||||
@ -44,30 +45,41 @@
|
||||
//! 1. `EnumString`: EnumString derives `std::str::FromStr` on the enum. Let's look an example of how the code is generated to
|
||||
//! see what various attributes do to generated code.
|
||||
//!
|
||||
//! ```dontrun
|
||||
//! ```
|
||||
//! # extern crate strum;
|
||||
//! # #[macro_use] extern crate strum_macros;
|
||||
//! #[derive(EnumString)]
|
||||
//! enum Color {
|
||||
//! Red,
|
||||
//! Green { range:usize }, // The Default value will be inserted into range.
|
||||
//! #[strum(serialize="blue",serialize="b")] // We can match on multiple different patterns.
|
||||
//!
|
||||
//! // 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),
|
||||
//! #[strum(disabled="true")] // Notice that we can disable certain variants from being found.
|
||||
//!
|
||||
//! // Notice that we can disable certain variants from being found
|
||||
//! #[strum(disabled="true")]
|
||||
//! Yellow,
|
||||
//! }
|
||||
//!
|
||||
//! // The generated code will look like:
|
||||
//! /*
|
||||
//! //The generated code will look like:
|
||||
//! impl std::str::FromStr for Color {
|
||||
//! type Err = strum::ParseError;
|
||||
//!
|
||||
//! fn from_str(s: &str) -> Result<#name #ty_generics,strum::ParseError> {
|
||||
//! fn from_str(s: &str) -> Result<Color, strum::ParseError> {
|
||||
//! match s {
|
||||
//! "Red" => Ok(Color::Red),
|
||||
//! "Green" => Ok(Green { range:Default::default(); }),
|
||||
//! "blue" | "b" => Ok(Blue(Default::default())),
|
||||
//! "Green" => Ok(Color::Green { range:Default::default() }),
|
||||
//! "blue" | "b" => Ok(Color::Blue(Default::default())),
|
||||
//! _ => Err(strum::ParseError::VariantNotFound),
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//! */
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! Notice how "Green" and "Blue" don't match on the associated data. That is intentional as parsing the input string
|
||||
@ -82,9 +94,12 @@
|
||||
//! of that to avoid potential naming collisions with this plugin. `EnumIter` implements the type `strum::IntoEnumIter` for
|
||||
//! your enum. This let's you write code like:
|
||||
//!
|
||||
//! ```dontrun
|
||||
//! ```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::IntoEnumIter;
|
||||
//! use strum::IntoEnumIterator;
|
||||
//!
|
||||
//! #[derive(EnumIter,Debug)]
|
||||
//! enum Color {
|
||||
@ -94,16 +109,27 @@
|
||||
//! Yellow,
|
||||
//! }
|
||||
//!
|
||||
//! // Generically print out all variants of an enum.
|
||||
//! // The 2nd bound is unpleasent looking, but can always be inferred.
|
||||
//! fn debug_enum<E,I:Iterator<Item=E>>() where E: IntoEnumIter<Iterator=I> {
|
||||
//! // It's simple to iterate over the variants of an enum.
|
||||
//! fn simple_example() {
|
||||
//! for color in Color::iter() {
|
||||
//! println!("My favorite color is {:?}", color);
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! // Iterating over any enum requires 2 type parameters
|
||||
//! // A 3rd is used in this example to allow passing a predicate
|
||||
//! fn generic_iterator<E, I, F>(pred: F)
|
||||
//! where E: IntoEnumIterator<Iterator=I>,
|
||||
//! I: Iterator<Item=E>,
|
||||
//! F: Fn(E) {
|
||||
//! for e in E::iter() {
|
||||
//! println!("{:?}", e);
|
||||
//! pred(e)
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! fn main() {
|
||||
//! debug_enum<Color,_>();
|
||||
//! simple_example();
|
||||
//! generic_iterator::<Color,_, _>(|color| println!("{:?}", color));
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
@ -116,7 +142,9 @@
|
||||
//! You can also provided a `#[strum(detailed_message="Here is a detailed message")]` attribute to create a
|
||||
//! seperate more detailed message than the first. Here's what code will be generated for you:
|
||||
//!
|
||||
//! ```dontrun
|
||||
//! ```rust
|
||||
//! # extern crate strum;
|
||||
//! # #[macro_use] extern crate strum_macros;
|
||||
//! // You need to bring the type into scope to use it!!!
|
||||
//! use strum::EnumMessage;
|
||||
//!
|
||||
@ -130,12 +158,13 @@
|
||||
//! 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"),
|
||||
//! &Color::Green {..} => Some("Simply Green"),
|
||||
//! _ => None
|
||||
//! }
|
||||
//! }
|
||||
@ -143,28 +172,30 @@
|
||||
//! fn get_detailed_message(&self) -> Option<&str> {
|
||||
//! match self {
|
||||
//! &Color::Red => Some("This is very red"),
|
||||
//! &Color::Green => Some("Simply Green"),
|
||||
//! &Color::Green {..}=> Some("Simply Green"),
|
||||
//! _ => None
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! fn get_serializations(&self) -> Option<&str> {
|
||||
//! fn get_serializations(&self) -> &[&str] {
|
||||
//! match self {
|
||||
//! &Color::Red => {
|
||||
//! static ARR: [&'static str; 1] = ["Red"];
|
||||
//! &ARR
|
||||
//! },
|
||||
//! &Color::Green => {
|
||||
//! &Color::Green {..}=> {
|
||||
//! static ARR: [&'static str; 1] = ["Green"];
|
||||
//! &ARR
|
||||
//! },
|
||||
//! &Color::Blue => {
|
||||
//! &Color::Blue (..) => {
|
||||
//! static ARR: [&'static str; 2] = ["b", "blue"];
|
||||
//! &ARR
|
||||
//! },
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//! */
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! # Using Strum Attributes
|
||||
@ -178,13 +209,13 @@
|
||||
//! - `default`: Can be set on a single variant in an enum of the form `Variant(T)` where `T: From<&str>`.
|
||||
//! This tells the plugin when it's generating the code for `FromStr()` to generate
|
||||
//!
|
||||
//! ```dontrun
|
||||
//! ```ignore
|
||||
//! default => Ok(Variant(default.into()))
|
||||
//! ```
|
||||
//!
|
||||
//! as the last line of the match statement instead of generating the usual code which is:
|
||||
//!
|
||||
//! ```dontrun
|
||||
//! ```ignore
|
||||
//! _ => Err(strum::ParseError::VariantNotFound)
|
||||
//! ```
|
||||
//!
|
||||
@ -205,33 +236,54 @@
|
||||
//!
|
||||
//! Using `EnumMessage` for quickly implementing `Error`
|
||||
//!
|
||||
//! ```dontrun
|
||||
//! ```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="There was an error in the network connection")]
|
||||
//! #[strum(message="A network error occured")]
|
||||
//! #[strum(detailed_message="Try checking your connection.")]
|
||||
//! NetworkError,
|
||||
//! #[strum(message="Could read the user input")]
|
||||
//! #[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_message().unwrap()
|
||||
//! self.get_detailed_message().unwrap()
|
||||
//! }
|
||||
//! }
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! Using `EnumString` to tokenize a series of inputs:
|
||||
//!
|
||||
//! ```dontrun
|
||||
//! ```rust
|
||||
//! extern crate strum;
|
||||
//! #[macro_use]
|
||||
//! extern crate strum_macros;
|
||||
//! use std::str::FromStr;
|
||||
//!
|
||||
//! #[derive(Debug, EnumString)]
|
||||
//! enum Tokens {
|
||||
//! #[strum(serialize="function")]
|
||||
//! Function,
|
||||
//! #[strum(serialize="("))]
|
||||
//! OpenParen
|
||||
//! #[strum(serialize=")"))]
|
||||
//! CloseParen
|
||||
//! #[strum(serialize="(")]
|
||||
//! OpenParen,
|
||||
//! #[strum(serialize=")")]
|
||||
//! CloseParen,
|
||||
//! #[strum(default="true")]
|
||||
//! Ident(String)
|
||||
//! }
|
||||
@ -247,10 +299,30 @@
|
||||
|
||||
/// The ParseError enum is a collection of all the possible reasons
|
||||
/// an enum can fail to parse from a string.
|
||||
#[derive(Debug,Clone,Copy,Eq,PartialEq,Hash)]
|
||||
pub enum ParseError {
|
||||
VariantNotFound,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ParseError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||
match self {
|
||||
&ParseError::VariantNotFound => write!(f, "Matching variant not found"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ParseError {
|
||||
fn description(&self) -> &str {
|
||||
match self {
|
||||
&ParseError::VariantNotFound => {
|
||||
"Unable to find a variant of the given enum matching the string given. Matching \
|
||||
can be extended with the Serialize attribute and is case sensitive."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This trait designates that an `Enum` can be iterated over. It can
|
||||
/// be auto generated using `strum_macros` on your behalf. The marker
|
||||
/// trait let's you program generically over any enum that can be
|
||||
|
@ -5,8 +5,8 @@ authors = ["Peter Glotfelty <peglotfe@microsoft.com>"]
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
name = "strum_macros"
|
||||
|
||||
[dependencies]
|
||||
quote = "*"
|
||||
syn = "*"
|
||||
strum = { git = "https://github.com/Peternator7/strum" }
|
||||
|
@ -1,6 +1,5 @@
|
||||
//! The strum_macros crate should be use in coordination with the `strum` crate.
|
||||
|
||||
extern crate strum;
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
@ -24,7 +23,6 @@ pub fn enum_iter(input: TokenStream) -> TokenStream {
|
||||
let ast = syn::parse_derive_input(&s).unwrap();
|
||||
|
||||
let toks = enum_iter_inner(&ast);
|
||||
println!("{}", toks);
|
||||
toks.parse().unwrap()
|
||||
}
|
||||
|
||||
@ -156,7 +154,7 @@ fn from_string_inner(ast: &syn::DeriveInput) -> quote::Tokens {
|
||||
quote!{
|
||||
impl #impl_generics std::str::FromStr for #name #ty_generics #where_clause {
|
||||
type Err = strum::ParseError;
|
||||
fn from_str(s: &str) -> Result<#name #ty_generics,strum::ParseError> {
|
||||
fn from_str(s: &str) -> Result< #name #ty_generics , strum::ParseError> {
|
||||
match s {
|
||||
#(#arms),*
|
||||
}
|
||||
@ -169,6 +167,8 @@ fn enum_iter_inner(ast: &syn::DeriveInput) -> quote::Tokens {
|
||||
let name = &ast.ident;
|
||||
let gen = &ast.generics;
|
||||
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
|
||||
let vis = &ast.vis;
|
||||
|
||||
if gen.lifetimes.len() > 0 {
|
||||
panic!("Enum Iterator isn't supported on Enums with lifetimes. The resulting enums would \
|
||||
be unbounded.");
|
||||
@ -219,7 +219,7 @@ fn enum_iter_inner(ast: &syn::DeriveInput) -> quote::Tokens {
|
||||
arms.push(quote! { _ => None });
|
||||
let iter_name = quote::Ident::from(&*format!("{}Iter", name));
|
||||
quote!{
|
||||
struct #iter_name #ty_generics {
|
||||
#vis struct #iter_name #ty_generics {
|
||||
idx: usize,
|
||||
marker: std::marker::PhantomData #phantom_data,
|
||||
}
|
||||
@ -262,8 +262,7 @@ fn enum_message_inner(ast: &syn::DeriveInput) -> quote::Tokens {
|
||||
let mut detailed_arms = Vec::new();
|
||||
let mut serializations = Vec::new();
|
||||
|
||||
let enabled = variants.iter().filter(|variant| !is_disabled(&variant.attrs));
|
||||
for variant in enabled {
|
||||
for variant in variants {
|
||||
let messages = unique_attr(&variant.attrs, "strum", "message");
|
||||
let detailed_messages = unique_attr(&variant.attrs, "strum", "detailed_message");
|
||||
let ident = &variant.ident;
|
||||
@ -275,6 +274,27 @@ fn enum_message_inner(ast: &syn::DeriveInput) -> quote::Tokens {
|
||||
Struct(..) => quote::Ident::from("{..}"),
|
||||
};
|
||||
|
||||
// You can't disable getting the serializations.
|
||||
{
|
||||
let mut serialization_variants = extract_attrs(&variant.attrs, "strum", "serialize");
|
||||
if serialization_variants.len() == 0 {
|
||||
serialization_variants.push(ident.as_ref());
|
||||
}
|
||||
|
||||
let count = serialization_variants.len();
|
||||
serializations.push(quote!{
|
||||
&#name::#ident #params => {
|
||||
static ARR: [&'static str; #count] = [#(#serialization_variants),*];
|
||||
&ARR
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// But you can disable the messages.
|
||||
if is_disabled(&variant.attrs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(msg) = messages {
|
||||
let params = params.clone();
|
||||
|
||||
@ -292,22 +312,6 @@ fn enum_message_inner(ast: &syn::DeriveInput) -> quote::Tokens {
|
||||
// Push the simple message.
|
||||
detailed_arms.push(quote!{ &#name::#ident #params => Some(#msg) });
|
||||
}
|
||||
|
||||
// Handle the serializations
|
||||
{
|
||||
let mut serialization_variants = extract_attrs(&variant.attrs, "strum", "serialize");
|
||||
if serialization_variants.len() == 0 {
|
||||
serialization_variants.push(ident.as_ref());
|
||||
}
|
||||
|
||||
let count = serialization_variants.len();
|
||||
serializations.push(quote!{
|
||||
&#name::#ident #params => {
|
||||
static ARR: [&'static str; #count] = [#(#serialization_variants),*];
|
||||
&ARR
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if arms.len() < variants.len() {
|
||||
|
@ -4,5 +4,5 @@ version = "0.1.0"
|
||||
authors = ["Peter Glotfelty <peglotfe@microsoft.com>"]
|
||||
|
||||
[dependencies]
|
||||
strum = { git = "https://github.com/Peternator7/strum" }
|
||||
strum_macros = { git = "https://github.com/Peternator7/strum" }
|
||||
strum = { path = "../strum" }
|
||||
strum_macros = { path = "../strum_macros" }
|
Loading…
Reference in New Issue
Block a user