1
0
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:
Peter Glotfelty 2017-02-11 11:42:32 -08:00
parent e9f28d8273
commit bbdceacd58
5 changed files with 141 additions and 61 deletions

View File

@ -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"

View File

@ -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

View File

@ -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" }

View File

@ -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() {

View File

@ -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" }