Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Useful changes to EnumString #368

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion strum_macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ heck = "0.5.0"
proc-macro2 = "1.0"
quote = "1.0"
rustversion = "1.0"
syn = { version = "2.0", features = ["parsing", "extra-traits"] }
syn = { version = "2.0", features = ["parsing", "extra-traits", "visit"] }

[dev-dependencies]
strum = { path = "../strum", version= "0.26" }
34 changes: 17 additions & 17 deletions strum_macros/src/helpers/case_style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,23 @@ impl CaseStyleHelpers for Ident {
}
}

/// heck doesn't treat numbers as new words, but this function does.
/// E.g. for input `Hello2You`, heck would output `hello2_you`, and snakify would output `hello_2_you`.
pub fn snakify(s: &str) -> String {
let mut output: Vec<char> = s.to_string().to_snake_case().chars().collect();
let mut num_starts = vec![];
for (pos, c) in output.iter().enumerate() {
if c.is_ascii_digit() && pos != 0 && !output[pos - 1].is_ascii_digit() {
num_starts.push(pos);
}
}
// need to do in reverse, because after inserting, all chars after the point of insertion are off
for i in num_starts.into_iter().rev() {
output.insert(i, '_')
}
output.into_iter().collect()
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -159,20 +176,3 @@ mod tests {
assert_eq!(MixedCase, f("mixed_case").unwrap());
}
}

/// heck doesn't treat numbers as new words, but this function does.
/// E.g. for input `Hello2You`, heck would output `hello2_you`, and snakify would output `hello_2_you`.
pub fn snakify(s: &str) -> String {
let mut output: Vec<char> = s.to_string().to_snake_case().chars().collect();
let mut num_starts = vec![];
for (pos, c) in output.iter().enumerate() {
if c.is_digit(10) && pos != 0 && !output[pos - 1].is_digit(10) {
num_starts.push(pos);
}
}
// need to do in reverse, because after inserting, all chars after the point of insertion are off
for i in num_starts.into_iter().rev() {
output.insert(i, '_')
}
output.into_iter().collect()
}
20 changes: 20 additions & 0 deletions strum_macros/src/helpers/lifetime_check.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use syn::visit::Visit;

#[derive(Default)]
struct LifetimeVisitor {
contains_lifetime: bool,
}

impl<'ast> Visit<'ast> for LifetimeVisitor {
fn visit_lifetime(&mut self, i: &'ast syn::Lifetime) {
self.contains_lifetime = true;
syn::visit::visit_lifetime(self, i);
}
}

pub fn contains_lifetime(ty: &syn::Type) -> bool {
let mut visitor = LifetimeVisitor::default();
visitor.visit_type(ty);

visitor.contains_lifetime
}
46 changes: 19 additions & 27 deletions strum_macros/src/helpers/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl Parse for EnumMeta {
}

pub enum EnumDiscriminantsMeta {
Derive { kw: kw::derive, paths: Vec<Path> },
Derive { _kw: kw::derive, paths: Vec<Path> },
Name { kw: kw::name, name: Ident },
Vis { kw: kw::vis, vis: Visibility },
Other { path: Path, nested: TokenStream },
Expand All @@ -96,12 +96,12 @@ pub enum EnumDiscriminantsMeta {
impl Parse for EnumDiscriminantsMeta {
fn parse(input: ParseStream) -> syn::Result<Self> {
if input.peek(kw::derive) {
let kw = input.parse()?;
let _kw = input.parse()?;
let content;
parenthesized!(content in input);
let paths = content.parse_terminated(Path::parse, Token![,])?;
Ok(EnumDiscriminantsMeta::Derive {
kw,
_kw,
paths: paths.into_iter().collect(),
})
} else if input.peek(kw::name) {
Expand Down Expand Up @@ -154,7 +154,7 @@ pub enum VariantMeta {
value: LitStr,
},
Serialize {
kw: kw::serialize,
_kw: kw::serialize,
value: LitStr,
},
Documentation {
Expand All @@ -175,66 +175,58 @@ pub enum VariantMeta {
value: bool,
},
Props {
kw: kw::props,
_kw: kw::props,
props: Vec<(LitStr, LitStr)>,
},
}

impl Parse for VariantMeta {
fn parse(input: ParseStream) -> syn::Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(kw::message) {
let kw = input.parse()?;
if let Ok(kw) = input.parse() {
let _: Token![=] = input.parse()?;
let value = input.parse()?;
Ok(VariantMeta::Message { kw, value })
} else if lookahead.peek(kw::detailed_message) {
let kw = input.parse()?;
} else if let Ok(kw) = input.parse() {
let _: Token![=] = input.parse()?;
let value = input.parse()?;
Ok(VariantMeta::DetailedMessage { kw, value })
} else if lookahead.peek(kw::serialize) {
let kw = input.parse()?;
} else if let Ok(_kw) = input.parse() {
let _: Token![=] = input.parse()?;
let value = input.parse()?;
Ok(VariantMeta::Serialize { kw, value })
} else if lookahead.peek(kw::to_string) {
let kw = input.parse()?;
Ok(VariantMeta::Serialize { _kw, value })
} else if let Ok(kw) = input.parse() {
let _: Token![=] = input.parse()?;
let value = input.parse()?;
Ok(VariantMeta::ToString { kw, value })
} else if lookahead.peek(kw::disabled) {
Ok(VariantMeta::Disabled(input.parse()?))
} else if lookahead.peek(kw::default) {
Ok(VariantMeta::Default(input.parse()?))
} else if lookahead.peek(kw::default_with) {
let kw = input.parse()?;
} else if let Ok(kw) = input.parse() {
Ok(VariantMeta::Disabled(kw))
} else if let Ok(kw) = input.parse() {
Ok(VariantMeta::Default(kw))
} else if let Ok(kw) = input.parse() {
let _: Token![=] = input.parse()?;
let value = input.parse()?;
Ok(VariantMeta::DefaultWith { kw, value })
} else if lookahead.peek(kw::ascii_case_insensitive) {
let kw = input.parse()?;
} else if let Ok(kw) = input.parse() {
let value = if input.peek(Token![=]) {
let _: Token![=] = input.parse()?;
input.parse::<LitBool>()?.value
} else {
true
};
Ok(VariantMeta::AsciiCaseInsensitive { kw, value })
} else if lookahead.peek(kw::props) {
let kw = input.parse()?;
} else if let Ok(_kw) = input.parse() {
let content;
parenthesized!(content in input);
let props = content.parse_terminated(Prop::parse, Token![,])?;
Ok(VariantMeta::Props {
kw,
_kw,
props: props
.into_iter()
.map(|Prop(k, v)| (LitStr::new(&k.to_string(), k.span()), v))
.collect(),
})
} else {
Err(lookahead.error())
Err(input.lookahead1().error())
}
}
}
Expand Down
1 change: 1 addition & 0 deletions strum_macros/src/helpers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub use self::variant_props::HasStrumVariantProperties;

pub mod case_style;
pub mod inner_variant_props;
pub mod lifetime_check;
mod metadata;
pub mod type_props;
pub mod variant_props;
Expand Down
3 changes: 1 addition & 2 deletions strum_macros/src/macros/enum_is.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,5 @@ pub fn enum_is_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
impl #impl_generics #enum_name #ty_generics #where_clause {
#(#variants)*
}
}
.into())
})
}
4 changes: 1 addition & 3 deletions strum_macros/src/macros/enum_try_as.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,7 @@ pub fn enum_try_as_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
}
})
},
_ => {
return None;
}
_ => None
}

})
Expand Down
26 changes: 15 additions & 11 deletions strum_macros/src/macros/strings/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ pub fn display_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
.enumerate()
.map(|(index, field)| {
assert!(field.ident.is_none());
let ident = syn::parse_str::<Ident>(format!("field{}", index).as_str()).unwrap();
let ident =
syn::parse_str::<Ident>(format!("field{}", index).as_str()).unwrap();
quote! { ref #ident }
})
.collect();
Expand Down Expand Up @@ -97,14 +98,14 @@ pub fn display_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
#name::#ident #params => ::core::fmt::Display::fmt(&format!(#output, #args), f)
}
}
},
}
Fields::Unnamed(ref unnamed_fields) => {
let used_vars = capture_format_strings(&output)?;
if used_vars.iter().any(String::is_empty) {
return Err(syn::Error::new_spanned(
&output,
"Empty {} is not allowed; Use manual numbering ({0})",
))
));
}
if used_vars.is_empty() {
quote! { #name::#ident #params => ::core::fmt::Display::fmt(#output, f) }
Expand Down Expand Up @@ -157,14 +158,17 @@ pub fn display_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
}

fn capture_format_string_idents(string_literal: &LitStr) -> syn::Result<Vec<Ident>> {
capture_format_strings(string_literal)?.into_iter().map(|ident| {
syn::parse_str::<Ident>(ident.as_str()).map_err(|_| {
syn::Error::new_spanned(
string_literal,
"Invalid identifier inside format string bracket",
)
capture_format_strings(string_literal)?
.into_iter()
.map(|ident| {
syn::parse_str::<Ident>(ident.as_str()).map_err(|_| {
syn::Error::new_spanned(
string_literal,
"Invalid identifier inside format string bracket",
)
})
})
}).collect()
.collect()
}

fn capture_format_strings(string_literal: &LitStr) -> syn::Result<Vec<String>> {
Expand Down Expand Up @@ -193,7 +197,7 @@ fn capture_format_strings(string_literal: &LitStr) -> syn::Result<Vec<String>> {
))?;

let inside_brackets = &format_str[start_index + 1..i];
let ident_str = inside_brackets.split(":").next().unwrap().trim_end();
let ident_str = inside_brackets.split(':').next().unwrap().trim_end();
var_used.push(ident_str.to_owned());
}
}
Expand Down
Loading