Skip to content

Commit

Permalink
Implemented ability to serialize_all using cases from heck.
Browse files Browse the repository at this point in the history
  • Loading branch information
azriel91 committed Sep 18, 2018
1 parent dbad703 commit 2e6b4a5
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 6 deletions.
1 change: 1 addition & 0 deletions strum_macros/Cargo.toml
Expand Up @@ -17,6 +17,7 @@ proc-macro = true
name = "strum_macros"

[dependencies]
heck = "0.3"
proc-macro2 = "0.4"
quote = "0.6"
syn = { version = "0.15", features = ["parsing"] }
34 changes: 34 additions & 0 deletions strum_macros/src/case_style.rs
@@ -0,0 +1,34 @@
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CaseStyle {
CamelCase,
KebabCase,
MixedCase,
ShoutySnakeCase,
SnakeCase,
TitleCase,
}

impl<'s> From<&'s str> for CaseStyle {
fn from(text: &'s str) -> CaseStyle {
match text {
"camel_case" => CaseStyle::CamelCase,
"kebab_case" => CaseStyle::KebabCase,
"mixed_case" => CaseStyle::MixedCase,
"shouty_snake_case" | "shouty_snek_case" => CaseStyle::ShoutySnakeCase,
"snake_case" | "snek_case" => CaseStyle::SnakeCase,
"title_case" => CaseStyle::TitleCase,
_ => panic!(
"Unexpected case style for serialize_all: `{}`. Valid values are: `{:?}`",
text,
[
"camel_case",
"kebab_case",
"mixed_case",
"shouty_snake_case",
"snake_case",
"title_case"
]
),
}
}
}
11 changes: 8 additions & 3 deletions strum_macros/src/from_string.rs
@@ -1,7 +1,8 @@
use proc_macro2::TokenStream;
use syn;

use helpers::{extract_attrs, extract_meta, is_disabled, unique_attr};
use case_style::CaseStyle;
use helpers::{convert_case, extract_attrs, extract_meta, is_disabled, unique_attr};

pub fn from_string_inner(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
Expand All @@ -11,6 +12,10 @@ pub fn from_string_inner(ast: &syn::DeriveInput) -> TokenStream {
_ => panic!("FromString only works on Enums"),
};

let type_meta = extract_meta(&ast.attrs);
let case_style = unique_attr(&type_meta, "strum", "serialize_all")
.map(|style| CaseStyle::from(style.as_ref()));

let mut has_default = false;
let mut default =
quote! { _ => ::std::result::Result::Err(::strum::ParseError::VariantNotFound) };
Expand Down Expand Up @@ -48,9 +53,9 @@ pub fn from_string_inner(ast: &syn::DeriveInput) -> TokenStream {
continue;
}

// If we don't have any custom variants, add the default name.
// If we don't have any custom variants, add the default serialized name.
if attrs.len() == 0 {
attrs.push(ident.to_string());
attrs.push(convert_case(ident, case_style));
}

let params = match variant.fields {
Expand Down
21 changes: 20 additions & 1 deletion strum_macros/src/helpers.rs
@@ -1,4 +1,7 @@
use syn::{Attribute, Meta};
use heck::{CamelCase, KebabCase, MixedCase, ShoutySnakeCase, SnakeCase, TitleCase};
use syn::{Attribute, Ident, Meta};

use case_style::CaseStyle;

pub fn extract_meta(attrs: &[Attribute]) -> Vec<Meta> {
attrs
Expand Down Expand Up @@ -55,3 +58,19 @@ pub fn is_disabled(attrs: &[Meta]) -> bool {
_ => panic!("Can't have multiple values for 'disabled'"),
}
}

pub fn convert_case(ident: &Ident, case_style: Option<CaseStyle>) -> String {
let ident_string = ident.to_string();
if let Some(case_style) = case_style {
match case_style {
CaseStyle::CamelCase => ident_string.to_camel_case(),
CaseStyle::KebabCase => ident_string.to_kebab_case(),
CaseStyle::MixedCase => ident_string.to_mixed_case(),
CaseStyle::ShoutySnakeCase => ident_string.to_shouty_snake_case(),
CaseStyle::SnakeCase => ident_string.to_snake_case(),
CaseStyle::TitleCase => ident_string.to_title_case(),
}
} else {
ident_string
}
}
2 changes: 2 additions & 0 deletions strum_macros/src/lib.rs
Expand Up @@ -9,13 +9,15 @@

#![recursion_limit = "128"]

extern crate heck;
extern crate syn;
#[macro_use]
extern crate quote;
extern crate proc_macro;
extern crate proc_macro2;

mod as_ref_str;
mod case_style;
mod display;
mod enum_iter;
mod enum_messages;
Expand Down
9 changes: 7 additions & 2 deletions strum_macros/src/to_string.rs
@@ -1,7 +1,8 @@
use proc_macro2::TokenStream;
use syn;

use helpers::{extract_attrs, extract_meta, is_disabled, unique_attr};
use case_style::CaseStyle;
use helpers::{convert_case, extract_attrs, extract_meta, is_disabled, unique_attr};

pub fn to_string_inner(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
Expand All @@ -11,6 +12,10 @@ pub fn to_string_inner(ast: &syn::DeriveInput) -> TokenStream {
_ => panic!("ToString only works on Enums"),
};

let type_meta = extract_meta(&ast.attrs);
let case_style = unique_attr(&type_meta, "strum", "serialize_all")
.map(|style| CaseStyle::from(style.as_ref()));

let mut arms = Vec::new();
for variant in variants {
use syn::Fields::*;
Expand All @@ -31,7 +36,7 @@ pub fn to_string_inner(ast: &syn::DeriveInput) -> TokenStream {
if let Some(n) = attrs.pop() {
n
} else {
ident.to_string()
convert_case(ident, case_style)
}
};

Expand Down
27 changes: 27 additions & 0 deletions strum_tests/tests/from_str.rs
Expand Up @@ -47,6 +47,33 @@ fn color_default() {
);
}

#[derive(Debug, Eq, PartialEq, EnumString)]
#[strum(serialize_all = "snake_case")]
enum Brightness {
DarkBlack,
Dim {
glow: usize,
},
#[strum(serialize = "Bright")]
BrightWhite,
}

#[test]
fn brightness_serialize_all() {
assert_eq!(
Brightness::DarkBlack,
Brightness::from_str("dark_black").unwrap()
);
assert_eq!(
Brightness::Dim { glow: 0 },
Brightness::from_str("dim").unwrap()
);
assert_eq!(
Brightness::BrightWhite,
Brightness::from_str("Bright").unwrap()
);
}

#[derive(Debug, Eq, PartialEq, EnumString)]
enum Week {
Sunday,
Expand Down
27 changes: 27 additions & 0 deletions strum_tests/tests/to_string.rs
Expand Up @@ -38,3 +38,30 @@ fn to_red_string() {
Color::from_str((Color::Red).to_string().as_ref()).unwrap()
);
}

#[derive(Debug, Eq, PartialEq, ToString)]
#[strum(serialize_all = "snake_case")]
enum Brightness {
DarkBlack,
Dim {
glow: usize,
},
#[strum(serialize = "bright")]
BrightWhite,
}

#[test]
fn brightness_to_string() {
assert_eq!(
String::from("dark_black"),
Brightness::DarkBlack.to_string().as_ref()
);
assert_eq!(
String::from("dim"),
Brightness::Dim { glow: 0 }.to_string().as_ref()
);
assert_eq!(
String::from("bright"),
Brightness::BrightWhite.to_string().as_ref()
);
}

0 comments on commit 2e6b4a5

Please sign in to comment.