Skip to content

Commit 7606dd5

Browse files
author
Peter Glotfelty
committed
Do some light refactoring to combine similiar code paths together
1 parent b6a357d commit 7606dd5

File tree

9 files changed

+173
-136
lines changed

9 files changed

+173
-136
lines changed

strum/src/additional_attributes.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@
7373
//! ```
7474
//! The plugin will fail if the data doesn't implement From<&str>. You can only have one `default`
7575
//! on your enum.
76-
//!
77-
//! - `transparent`: Signals that the inner field's implementation should be used, instead of generating
78-
//! one for this variant. Only applicable to enum variants with a single field. Compatible with the
76+
//!
77+
//! - `transparent`: Signals that the inner field's implementation should be used, instead of generating
78+
//! one for this variant. Only applicable to enum variants with a single field. Compatible with the
7979
//! `AsRefStr`, `Display` and `IntoStaticStr` derive macros.
8080
//!
8181
//! - `disabled`: removes variant from generated code.

strum_macros/src/helpers/metadata.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ pub enum EnumMeta {
6363
kw: kw::parse_err_fn,
6464
path: Path,
6565
},
66-
ConstIntoStr(kw::const_into_str)
66+
ConstIntoStr(kw::const_into_str),
6767
}
6868

6969
impl Parse for EnumMeta {

strum_macros/src/helpers/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ pub fn non_unit_variant_error() -> syn::Error {
3535
pub fn non_single_field_variant_error(attr: &str) -> syn::Error {
3636
syn::Error::new(
3737
Span::call_site(),
38-
format_args!("The [`{}`] attribute only supports enum variants with a single field", attr),
38+
format_args!(
39+
"The [`{}`] attribute only supports enum variants with a single field",
40+
attr
41+
),
3942
)
4043
}
4144

strum_macros/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ fn debug_print_generated(ast: &DeriveInput, toks: &TokenStream) {
5656
///
5757
/// The default error type is `strum::ParseError`. This can be overriden by applying both the
5858
/// `parse_err_ty` and `parse_err_fn` attributes at the type level. `parse_error_fn` should be a
59-
/// function that accepts an `&str` and returns the type `parse_error_ty`. See
59+
/// function that accepts an `&str` and returns the type `parse_error_ty`. See
6060
/// [this test case](https://github.com/Peternator7/strum/blob/9db3c4dc9b6f585aeb9f5f15f9cc18b6cf4fd780/strum_tests/tests/from_str.rs#L233)
6161
/// for an example.
6262
///

strum_macros/src/macros/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,4 @@ mod strings;
1515
pub use self::strings::as_ref_str;
1616
pub use self::strings::display;
1717
pub use self::strings::from_string;
18-
pub use self::strings::into_static_str;
1918
pub use self::strings::to_string;

strum_macros/src/macros/strings/as_ref_str.rs

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ use crate::helpers::{
66
non_enum_error, non_single_field_variant_error, HasStrumVariantProperties, HasTypeProperties,
77
};
88

9-
fn get_arms(ast: &DeriveInput) -> syn::Result<Vec<TokenStream>> {
9+
fn get_arms<F>(ast: &DeriveInput, transparent_fn: F) -> syn::Result<Vec<TokenStream>>
10+
where
11+
F: Fn(&TokenStream) -> TokenStream,
12+
{
1013
let name = &ast.ident;
1114
let mut arms = Vec::new();
1215
let variants = match &ast.data {
@@ -24,35 +27,28 @@ fn get_arms(ast: &DeriveInput) -> syn::Result<Vec<TokenStream>> {
2427
continue;
2528
}
2629

27-
let arm = if variant_properties.transparent.is_some() {
28-
let arm_end = match &variant.fields {
29-
Fields::Unnamed(f) if f.unnamed.len() == 1 => {
30-
quote! { (ref v) => ::core::convert::AsRef::<str>::as_ref(v) }
31-
}
32-
Fields::Named(f) if f.named.len() == 1 => {
33-
let ident = f.named.last().unwrap().ident.as_ref().unwrap();
34-
quote! { {ref #ident} => ::core::convert::AsRef::<str>::as_ref(#ident) }
35-
}
36-
_ => return Err(non_single_field_variant_error("transparent")),
37-
};
38-
39-
quote! { #name::#ident #arm_end }
40-
} else {
41-
// Look at all the serialize attributes.
42-
// Use `to_string` attribute (not `as_ref_str` or something) to keep things consistent
43-
// (i.e. always `enum.as_ref().to_string() == enum.to_string()`).
44-
let output = variant_properties
45-
.get_preferred_name(type_properties.case_style, type_properties.prefix.as_ref());
46-
let params = match variant.fields {
47-
Fields::Unit => quote! {},
48-
Fields::Unnamed(..) => quote! { (..) },
49-
Fields::Named(..) => quote! { {..} },
50-
};
51-
52-
quote! { #name::#ident #params => #output }
30+
if let Some(..) = variant_properties.transparent {
31+
let arm = super::extract_single_field_variant_and_then(name, variant, |tok| {
32+
transparent_fn(tok)
33+
})
34+
.map_err(|_| non_single_field_variant_error("transparent"))?;
35+
36+
arms.push(arm);
37+
continue;
38+
}
39+
40+
// Look at all the serialize attributes.
41+
// Use `to_string` attribute (not `as_ref_str` or something) to keep things consistent
42+
// (i.e. always `enum.as_ref().to_string() == enum.to_string()`).
43+
let output = variant_properties
44+
.get_preferred_name(type_properties.case_style, type_properties.prefix.as_ref());
45+
let params = match variant.fields {
46+
Fields::Unit => quote! {},
47+
Fields::Unnamed(..) => quote! { (..) },
48+
Fields::Named(..) => quote! { {..} },
5349
};
5450

55-
arms.push(arm);
51+
arms.push(quote! { #name::#ident #params => #output });
5652
}
5753

5854
if arms.len() < variants.len() {
@@ -70,7 +66,10 @@ fn get_arms(ast: &DeriveInput) -> syn::Result<Vec<TokenStream>> {
7066
pub fn as_ref_str_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
7167
let name = &ast.ident;
7268
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
73-
let arms = get_arms(ast)?;
69+
let arms = get_arms(ast, |tok| {
70+
quote! { ::core::convert::AsRef::<str>::as_ref(#tok) }
71+
})?;
72+
7473
Ok(quote! {
7574
impl #impl_generics ::core::convert::AsRef<str> for #name #ty_generics #where_clause {
7675
#[inline]
@@ -94,7 +93,10 @@ pub fn as_static_str_inner(
9493
) -> syn::Result<TokenStream> {
9594
let name = &ast.ident;
9695
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
97-
let arms = get_arms(ast)?;
96+
let arms = get_arms(ast, |tok| {
97+
quote! { ::core::convert::From::from(#tok) }
98+
})?;
99+
98100
let type_properties = ast.get_type_properties()?;
99101
let strum_module_path = type_properties.crate_module_path();
100102

strum_macros/src/macros/strings/display.rs

Lines changed: 92 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,13 @@ pub fn display_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
2525
continue;
2626
}
2727

28-
if variant_properties.transparent.is_some() {
29-
let arm_end = match &variant.fields {
30-
Fields::Unnamed(f) if f.unnamed.len() == 1 => {
31-
quote! { (ref v) => ::core::fmt::Display::fmt(v, f)}
32-
}
33-
Fields::Named(f) if f.named.len() == 1 => {
34-
let ident = f.named.last().unwrap().ident.as_ref().unwrap();
35-
quote! { {ref #ident} => ::core::fmt::Display::fmt(#ident, f) }
36-
}
37-
_ => return Err(non_single_field_variant_error("transparent")),
38-
};
28+
if let Some(..) = variant_properties.transparent {
29+
let arm = super::extract_single_field_variant_and_then(name, variant, |tok| {
30+
quote! { ::core::fmt::Display::fmt(#tok, f)}
31+
})
32+
.map_err(|_| non_single_field_variant_error("transparent"))?;
3933

40-
arms.push(quote! { #name::#ident #arm_end });
34+
arms.push(arm);
4135
continue;
4236
}
4337

@@ -55,7 +49,8 @@ pub fn display_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
5549
.enumerate()
5650
.map(|(index, field)| {
5751
assert!(field.ident.is_none());
58-
let ident = syn::parse_str::<Ident>(format!("field{}", index).as_str()).unwrap();
52+
let ident =
53+
syn::parse_str::<Ident>(format!("field{}", index).as_str()).unwrap();
5954
quote! { ref #ident }
6055
})
6156
.collect();
@@ -77,86 +72,87 @@ pub fn display_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
7772
};
7873

7974
if variant_properties.to_string.is_none() && variant_properties.default.is_some() {
80-
match &variant.fields {
81-
Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
82-
arms.push(quote! { #name::#ident(ref s) => ::core::fmt::Display::fmt(s, f) });
75+
let arm = super::extract_single_field_variant_and_then(name, variant, |tok| {
76+
quote! { ::core::fmt::Display::fmt(#tok, f)}
77+
})
78+
.map_err(|_| {
79+
syn::Error::new_spanned(
80+
variant,
81+
"Default only works on newtype structs with a single String field",
82+
)
83+
})?;
84+
85+
arms.push(arm);
86+
continue;
87+
}
88+
89+
let arm = match variant.fields {
90+
Fields::Named(ref field_names) => {
91+
let used_vars = capture_format_string_idents(&output)?;
92+
if used_vars.is_empty() {
93+
quote! { #name::#ident #params => ::core::fmt::Display::fmt(#output, f) }
94+
} else {
95+
// Create args like 'name = name, age = age' for format macro
96+
let args: Punctuated<_, Token!(,)> = field_names
97+
.named
98+
.iter()
99+
.filter_map(|field| {
100+
let ident = field.ident.as_ref().unwrap();
101+
// Only contain variables that are used in format string
102+
if !used_vars.contains(ident) {
103+
None
104+
} else {
105+
Some(quote! { #ident = #ident })
106+
}
107+
})
108+
.collect();
109+
110+
quote! {
111+
#[allow(unused_variables)]
112+
#name::#ident #params => ::core::fmt::Display::fmt(&format_args!(#output, #args), f)
113+
}
83114
}
84-
_ => {
115+
}
116+
Fields::Unnamed(ref unnamed_fields) => {
117+
let used_vars = capture_format_strings(&output)?;
118+
if used_vars.iter().any(String::is_empty) {
85119
return Err(syn::Error::new_spanned(
86-
variant,
87-
"Default only works on newtype structs with a single String field",
88-
))
120+
&output,
121+
"Empty {} is not allowed; Use manual numbering ({0})",
122+
));
89123
}
90-
}
91-
} else {
92-
let arm = match variant.fields {
93-
Fields::Named(ref field_names) => {
94-
let used_vars = capture_format_string_idents(&output)?;
95-
if used_vars.is_empty() {
96-
quote! { #name::#ident #params => ::core::fmt::Display::fmt(#output, f) }
97-
} else {
98-
// Create args like 'name = name, age = age' for format macro
99-
let args: Punctuated<_, Token!(,)> = field_names
100-
.named
101-
.iter()
102-
.filter_map(|field| {
103-
let ident = field.ident.as_ref().unwrap();
104-
// Only contain variables that are used in format string
105-
if !used_vars.contains(ident) {
106-
None
107-
} else {
108-
Some(quote! { #ident = #ident })
109-
}
110-
})
111-
.collect();
112-
113-
quote! {
114-
#[allow(unused_variables)]
115-
#name::#ident #params => ::core::fmt::Display::fmt(&format_args!(#output, #args), f)
116-
}
117-
}
118-
},
119-
Fields::Unnamed(ref unnamed_fields) => {
120-
let used_vars = capture_format_strings(&output)?;
121-
if used_vars.iter().any(String::is_empty) {
122-
return Err(syn::Error::new_spanned(
123-
&output,
124-
"Empty {} is not allowed; Use manual numbering ({0})",
125-
))
126-
}
127-
if used_vars.is_empty() {
128-
quote! { #name::#ident #params => ::core::fmt::Display::fmt(#output, f) }
129-
} else {
130-
let args: Punctuated<_, Token!(,)> = unnamed_fields
131-
.unnamed
132-
.iter()
133-
.enumerate()
134-
.map(|(index, field)| {
135-
assert!(field.ident.is_none());
136-
syn::parse_str::<Ident>(format!("field{}", index).as_str()).unwrap()
137-
})
138-
.collect();
139-
quote! {
140-
#[allow(unused_variables)]
141-
#name::#ident #params => ::core::fmt::Display::fmt(&format!(#output, #args), f)
142-
}
124+
if used_vars.is_empty() {
125+
quote! { #name::#ident #params => ::core::fmt::Display::fmt(#output, f) }
126+
} else {
127+
let args: Punctuated<_, Token!(,)> = unnamed_fields
128+
.unnamed
129+
.iter()
130+
.enumerate()
131+
.map(|(index, field)| {
132+
assert!(field.ident.is_none());
133+
syn::parse_str::<Ident>(format!("field{}", index).as_str()).unwrap()
134+
})
135+
.collect();
136+
quote! {
137+
#[allow(unused_variables)]
138+
#name::#ident #params => ::core::fmt::Display::fmt(&format!(#output, #args), f)
143139
}
144140
}
145-
Fields::Unit => {
146-
let used_vars = capture_format_strings(&output)?;
147-
if !used_vars.is_empty() {
148-
return Err(syn::Error::new_spanned(
149-
&output,
150-
"Unit variants do not support interpolation",
151-
));
152-
}
153-
154-
quote! { #name::#ident #params => ::core::fmt::Display::fmt(#output, f) }
141+
}
142+
Fields::Unit => {
143+
let used_vars = capture_format_strings(&output)?;
144+
if !used_vars.is_empty() {
145+
return Err(syn::Error::new_spanned(
146+
&output,
147+
"Unit variants do not support interpolation",
148+
));
155149
}
156-
};
157150

158-
arms.push(arm);
159-
}
151+
quote! { #name::#ident #params => ::core::fmt::Display::fmt(#output, f) }
152+
}
153+
};
154+
155+
arms.push(arm);
160156
}
161157

162158
if arms.len() < variants.len() {
@@ -175,14 +171,17 @@ pub fn display_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
175171
}
176172

177173
fn capture_format_string_idents(string_literal: &LitStr) -> syn::Result<Vec<Ident>> {
178-
capture_format_strings(string_literal)?.into_iter().map(|ident| {
179-
syn::parse_str::<Ident>(ident.as_str()).map_err(|_| {
180-
syn::Error::new_spanned(
181-
string_literal,
182-
"Invalid identifier inside format string bracket",
183-
)
174+
capture_format_strings(string_literal)?
175+
.into_iter()
176+
.map(|ident| {
177+
syn::parse_str::<Ident>(ident.as_str()).map_err(|_| {
178+
syn::Error::new_spanned(
179+
string_literal,
180+
"Invalid identifier inside format string bracket",
181+
)
182+
})
184183
})
185-
}).collect()
184+
.collect()
186185
}
187186

188187
fn capture_format_strings(string_literal: &LitStr) -> syn::Result<Vec<String>> {

0 commit comments

Comments
 (0)