1
0
mirror of https://github.com/danog/strum.git synced 2024-11-26 20:14:40 +01:00

Add derive(AsRefStr) implementation (#10)

Implements AsRef<str> as a non-allocating alternative to deriving ToString. Docs will come later
This commit is contained in:
らりお (YOSHIOKA Takuma) 2017-08-24 11:18:23 +09:00 committed by Peter Glotfelty
parent c9bba73f9b
commit af58a6d403
5 changed files with 121 additions and 1 deletions

View File

@ -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),*
}
}
}
}
}

View File

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

View File

@ -57,4 +57,4 @@ pub fn to_string_inner(ast: &syn::DeriveInput) -> quote::Tokens {
}
}
}
}
}

View File

@ -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());
}

View File

@ -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());
}