Repository: Peternator7/strum
Pull request #10 (2017-08-19 18:29:00)

Add `derive(AsRefStr)` implementation
* `derive(AsRefStr)` implements `AsRef<str>` (UPDATED: 558cdd0cd018dfe3a1b4fd8f8636ab514ac4cfd7)
* ~~`derive(AsStr)` uses `as_str`, `to_string`, `serialize` attributes (in this order).~~
* ~~`derive(ToString)` uses `to_string`, `as_str`, `serialize` attributes (in this order).~~
* `derive(AsRefStr)` uses `to_string` and `serialize` attributes (in this order, same as `derive(ToString)`). (UPDATED: a1b480dfc8c5e0bae9e07aed8821da4f1710383c, 558cdd0cd018dfe3a1b4fd8f8636ab514ac4cfd7)

I don't know which is better: `AsRef<str> for Enum` vs. `Enum::as_str()`, but I used `AsRef<str>` because it is similar to `ToString` (both are traits and therefore non-invasive).

Also I can't decide which name is better, `AsStr` vs. `AsRefStr`.

README documentation is not updated in this PR...

This solves #9.

lo48576: Ok, I'll rewrite the code to use `derive(AsRefStr)` and common `to_string` attribute.
lo48576: Removed `as_str` attribute and renamed `AsStr` to `AsRefStr`.

I'm sorry `to_string.rs` (https://github.com/Peternator7/strum/pull/10/files#diff-7eb81bb78680f1333200921a515022c5) has no content changes but trailing newline added...

Patch:
diff --git a/strum_macros/src/as_ref_str.rs b/strum_macros/src/as_ref_str.rs
new file mode 100644
index 0000000..e060347
--- /dev/null
+++ b/strum_macros/src/as_ref_str.rs
@@ -0,0 +1,62 @@
+
+use quote;
+use syn;
+
+use helpers::{unique_attr, extract_attrs, is_disabled};
+
+pub fn as_ref_str_inner(ast: &syn::DeriveInput) -> quote::Tokens {
+    let name = &ast.ident;
+    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
+    let variants = match ast.body {
+        syn::Body::Enum(ref v) => v,
+        _ => panic!("AsRefStr only works on Enums"),
+    };
+
+    let mut arms = Vec::new();
+    for variant in variants {
+        use syn::VariantData::*;
+        let ident = &variant.ident;
+
+        if is_disabled(&variant.attrs) {
+            continue;
+        }
+
+        // Look at all the serialize attributes.
+        // Use `to_string` attribute (not `as_ref_str` or something) to keep things consistent
+        // (i.e. always `enum.as_ref().to_string() == enum.to_string()`).
+        let output = if let Some(n) = unique_attr(&variant.attrs, "strum", "to_string") {
+            n
+        } else {
+            let mut attrs = extract_attrs(&variant.attrs, "strum", "serialize");
+            // We always take the longest one. This is arbitary, but is *mostly* deterministic
+            attrs.sort_by_key(|s| -(s.len() as isize));
+            if let Some(n) = attrs.first() {
+                n
+            } else {
+                ident.as_ref()
+            }
+        };
+
+        let params = match variant.data {
+            Unit => quote::Ident::from(""),
+            Tuple(..) => quote::Ident::from("(..)"),
+            Struct(..) => quote::Ident::from("{..}"),
+        };
+
+        arms.push(quote!{ #name::#ident #params => #output });
+    }
+
+    if arms.len() < variants.len() {
+        arms.push(quote!{ _ => panic!("AsRef<str>::as_ref() called on disabled variant.")})
+    }
+
+    quote!{
+        impl #impl_generics ::std::convert::AsRef<str> for #name #ty_generics #where_clause {
+            fn as_ref(&self) -> &str {
+                match *self {
+                    #(#arms),*
+                }
+            }
+        }
+    }
+}
diff --git a/strum_macros/src/lib.rs b/strum_macros/src/lib.rs
index c25861b..62f2108 100644
--- a/strum_macros/src/lib.rs
+++ b/strum_macros/src/lib.rs
@@ -13,6 +13,7 @@ extern crate quote;
 extern crate proc_macro;
 
 mod helpers;
+mod as_ref_str;
 mod to_string;
 mod from_string;
 mod enum_iter;
@@ -46,6 +47,16 @@ pub fn from_string(input: TokenStream) -> TokenStream {
     toks.parse().unwrap()
 }
 
+#[proc_macro_derive(AsRefStr,attributes(strum))]
+pub fn as_ref_str(input: TokenStream) -> TokenStream {
+    let s = input.to_string();
+    let ast = syn::parse_derive_input(&s).unwrap();
+
+    let toks = as_ref_str::as_ref_str_inner(&ast);
+    debug_print_generated(&ast, &toks);
+    toks.parse().unwrap()
+}
+
 #[proc_macro_derive(ToString,attributes(strum))]
 pub fn to_string(input: TokenStream) -> TokenStream {
     let s = input.to_string();
diff --git a/strum_macros/src/to_string.rs b/strum_macros/src/to_string.rs
index 256cb90..8eae679 100644
--- a/strum_macros/src/to_string.rs
+++ b/strum_macros/src/to_string.rs
@@ -57,4 +57,4 @@ pub fn to_string_inner(ast: &syn::DeriveInput) -> quote::Tokens {
             }
         }
     }
-}
\ No newline at end of file
+}


Test patch:
diff --git a/strum_tests/tests/as_ref_str.rs b/strum_tests/tests/as_ref_str.rs
new file mode 100644
index 0000000..a7a54cf
--- /dev/null
+++ b/strum_tests/tests/as_ref_str.rs
@@ -0,0 +1,46 @@
+extern crate strum;
+#[macro_use]
+extern crate strum_macros;
+
+use std::str::FromStr;
+
+#[derive(Debug,Eq,PartialEq,EnumString,AsRefStr)]
+enum Color {
+    #[strum(to_string="RedRed")]
+    Red,
+    #[strum(serialize="b", to_string="blue")]
+    Blue { hue: usize },
+    #[strum(serialize="y", serialize="yellow")]
+    Yellow,
+    #[strum(default="true")]
+    Green(String),
+}
+
+#[test]
+fn color_simple() {
+    assert_eq!(Color::Red, Color::from_str("RedRed").unwrap());
+}
+
+#[test]
+fn as_red_str() {
+    assert_eq!("RedRed",
+               (Color::Red).as_ref());
+    assert_eq!(Color::Red,
+               Color::from_str((Color::Red).as_ref()).unwrap());
+}
+
+#[test]
+fn as_blue_str() {
+    assert_eq!("blue",
+               (Color::Blue { hue: 0 }).as_ref());
+}
+
+#[test]
+fn as_yellow_str() {
+    assert_eq!("yellow", (Color::Yellow).as_ref());
+}
+
+#[test]
+fn as_green_str() {
+    assert_eq!("Green", (Color::Green(String::default())).as_ref());
+}
diff --git a/strum_tests/tests/to_string.rs b/strum_tests/tests/to_string.rs
index 2364971..1847f85 100644
--- a/strum_tests/tests/to_string.rs
+++ b/strum_tests/tests/to_string.rs
@@ -35,6 +35,7 @@ fn to_yellow_string() {
 
 #[test]
 fn to_red_string() {
+    assert_eq!(String::from("RedRed"), (Color::Red).to_string());
     assert_eq!(Color::Red,
                Color::from_str((Color::Red).to_string().as_ref()).unwrap());
 }