diff --git a/strum/Cargo.toml b/strum/Cargo.toml index c745468..d51b111 100644 --- a/strum/Cargo.toml +++ b/strum/Cargo.toml @@ -3,4 +3,8 @@ name = "strum" version = "0.1.0" authors = ["Peter Glotfelty "] -[dependencies] +[dev-dependencies] +strum_macros = { path = "../strum_macros" } + +[lib] +name = "strum" \ No newline at end of file diff --git a/strum/src/lib.rs b/strum/src/lib.rs index e2c3a90..438f7ec 100644 --- a/strum/src/lib.rs +++ b/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 { //! 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>() where E: IntoEnumIter { +//! // 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(pred: F) +//! where E: IntoEnumIterator, +//! I: Iterator, +//! F: Fn(E) { //! for e in E::iter() { -//! println!("{:?}", e); +//! pred(e) //! } //! } //! //! fn main() { -//! debug_enum(); +//! simple_example(); +//! generic_iterator::(|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 diff --git a/strum_macros/Cargo.toml b/strum_macros/Cargo.toml index 0788713..ead0613 100644 --- a/strum_macros/Cargo.toml +++ b/strum_macros/Cargo.toml @@ -5,8 +5,8 @@ authors = ["Peter Glotfelty "] [lib] proc-macro = true +name = "strum_macros" [dependencies] quote = "*" syn = "*" -strum = { git = "https://github.com/Peternator7/strum" } diff --git a/strum_macros/src/lib.rs b/strum_macros/src/lib.rs index 2a2e0c8..3f3a3db 100644 --- a/strum_macros/src/lib.rs +++ b/strum_macros/src/lib.rs @@ -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() { diff --git a/strum_tests/Cargo.toml b/strum_tests/Cargo.toml index 0c8d802..dff51d0 100644 --- a/strum_tests/Cargo.toml +++ b/strum_tests/Cargo.toml @@ -4,5 +4,5 @@ version = "0.1.0" authors = ["Peter Glotfelty "] [dependencies] -strum = { git = "https://github.com/Peternator7/strum" } -strum_macros = { git = "https://github.com/Peternator7/strum" } \ No newline at end of file +strum = { path = "../strum" } +strum_macros = { path = "../strum_macros" } \ No newline at end of file