Skip to content
Merged
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
206 changes: 204 additions & 2 deletions src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ where
}

/// Represents a rename attribute for an enum variant.
#[derive(Clone)]
#[derive(Clone, Debug, PartialEq)]
struct VariantRename(String);

impl TryFrom<(String, String)> for VariantRename {
Expand Down Expand Up @@ -98,7 +98,7 @@ impl VariantRename {
}

/// Represents attribute configurations for renaming enum variants.
#[derive(Default)]
#[derive(Default, Debug, PartialEq)]
pub(crate) struct Attributes {
case: Option<Case>,
prefix: Option<String>,
Expand Down Expand Up @@ -211,3 +211,205 @@ impl Variants {
.collect()
}
}

#[cfg(test)]
mod tests {
use quote::quote;

use super::*;

#[test]
fn test_parse_string() {
assert_eq!(parse_string("\"hello\""), Ok("hello".to_string()));
assert_eq!(
parse_string("hello"),
Err("String must be enclosed in double quotes")
);

assert_eq!(
parse_string("\"hello"),
Err("String must be enclosed in double quotes")
);
assert_eq!(
parse_string("hello\""),
Err("String must be enclosed in double quotes")
);
assert_eq!(parse_string("\"he\"llo\""), Ok("he\"llo".to_string()));

assert_eq!(parse_string("\"\""), Ok("".to_string()));
assert_eq!(parse_string("\"\"\""), Ok("\"".to_string()));
}

fn assert_parse_token_list<T>(tokens: TokenStream, expected: Vec<T>)
where
T: TryFrom<(String, String)> + PartialEq + std::fmt::Debug,
{
let result = parse_token_list::<T>(&tokens).unwrap();
assert_eq!(result, expected);
}

#[test]
fn test_parse_token_list_string_string() {
assert_parse_token_list::<(String, String)>(quote! {}, vec![]);

assert_parse_token_list::<(String, String)>(
quote! { rename = "hello" },
vec![("rename".to_string(), "\"hello\"".to_string())],
);

assert_parse_token_list(
quote! { rename = "hello", rename = "world" },
vec![
("rename".to_string(), "\"hello\"".to_string()),
("rename".to_string(), "\"world\"".to_string()),
],
);
}

#[derive(Debug, PartialEq)]
struct TestStruct(String);

impl TryFrom<(String, String)> for TestStruct {
type Error = &'static str;

fn try_from(value: (String, String)) -> Result<Self, Self::Error> {
Ok(Self(parse_string(value.1.as_str())?))
}
}

#[test]
fn test_parse_token_list_struct() {
assert_parse_token_list::<TestStruct>(
quote! { rename = "hello" },
vec![TestStruct("hello".to_string())],
);

assert_parse_token_list::<TestStruct>(
quote! { rename = "hello", rename = "world" },
vec![
TestStruct("hello".to_string()),
TestStruct("world".to_string()),
],
);
}

#[test]
fn test_variant_rename_try_from() {
assert_eq!(
VariantRename::try_from(("rename".to_string(), "\"hello\"".to_string())),
Ok(VariantRename("hello".to_string()))
);

assert_eq!(
VariantRename::try_from(("rename".to_string(), "hello".to_string())),
Err("String must be enclosed in double quotes")
);

assert_eq!(
VariantRename::try_from(("not_rename".to_string(), "\"hello\"".to_string())),
Err("Not a rename string")
);
}

#[test]
fn test_parse_token_list_variant_rename() {
assert_parse_token_list::<VariantRename>(
quote! { rename = "hello" },
vec![VariantRename("hello".to_string())],
);

assert_parse_token_list::<VariantRename>(
quote! { rename = "hello", rename = "world" },
vec![
VariantRename("hello".to_string()),
VariantRename("world".to_string()),
],
);
}

#[test]
fn test_attributes_parse_args() {
let attribute =
syn::parse_quote! { #[enum_stringify(prefix = "pre", suffix = "suf", case = "snake")] };
let attributes = Attributes::parse_args(&attribute).unwrap();
assert_eq!(attributes.prefix, Some("pre".to_string()));
assert_eq!(attributes.suffix, Some("suf".to_string()));
assert_eq!(
attributes.case.map(|a| a.to_string()),
Some("snake".to_string())
);

let attribute = syn::parse_quote! { #[enum_stringify(prefix = "pre", suffix = "suf")] };
let attributes = Attributes::parse_args(&attribute).unwrap();
assert_eq!(attributes.prefix, Some("pre".to_string()));
assert_eq!(attributes.suffix, Some("suf".to_string()));
assert_eq!(attributes.case, None);

let attribute = syn::parse_quote! { #[enum_stringify(case = "snake")] };
let attributes = Attributes::parse_args(&attribute).unwrap();
assert_eq!(attributes.prefix, None);
assert_eq!(attributes.suffix, None);
assert_eq!(
attributes.case.map(|a| a.to_string()),
Some("snake".to_string())
);

let attribute = syn::parse_quote! { #[enum_stringify] };
assert_eq!(Attributes::parse_args(&attribute), None);
}

#[test]
fn test_attributes_update_attribute() {
let mut attributes = Attributes::default();
attributes.update_attribute(("prefix".to_string(), "\"pre\"".to_string()));
assert_eq!(attributes.prefix, Some("pre".to_string()));

attributes.update_attribute(("suffix".to_string(), "\"suf\"".to_string()));
assert_eq!(attributes.suffix, Some("suf".to_string()));

attributes.update_attribute(("case".to_string(), "\"snake\"".to_string()));
assert_eq!(
attributes.case.clone().map(|a| a.to_string()),
Some("snake".to_string())
);

attributes.update_attribute(("invalid".to_string(), "\"value\"".to_string()));
assert_eq!(attributes.prefix, Some("pre".to_string()));
assert_eq!(attributes.suffix, Some("suf".to_string()));
assert_eq!(
attributes.case.clone().map(|a| a.to_string()),
Some("snake".to_string())
);

attributes.update_attribute(("prefix".to_string(), "\"new1\"".to_string()));
assert_eq!(attributes.prefix, Some("new1".to_string()));

attributes.update_attribute(("suffix".to_string(), "\"new2\"".to_string()));
assert_eq!(attributes.suffix, Some("new2".to_string()));

attributes.update_attribute(("case".to_string(), "\"upper\"".to_string()));
assert_eq!(
attributes.case.clone().map(|a| a.to_string()),
Some("upper".to_string())
);
}

#[test]
fn test_attributes_rename() {
let mut attributes = Attributes {
prefix: Some("pre".to_string()),
suffix: Some("suf".to_string()),
case: None,
};

assert_eq!(attributes.rename("name"), "prenamesuf");
assert_eq!(attributes.rename("Name"), "preNamesuf");
assert_eq!(attributes.rename("NAME"), "preNAMEsuf");

attributes.update_attribute(("case".to_string(), "\"upper_flat\"".to_string()));

assert_eq!(attributes.rename("name"), "PRENAMESUF");
assert_eq!(attributes.rename("Name"), "PRENAMESUF");
assert_eq!(attributes.rename("NAME"), "PRENAMESUF");
}
}
28 changes: 28 additions & 0 deletions src/case.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use std::fmt::Display;

use convert_case::Casing;

/// Wrapper struct around `convert_case::Case` to represent different casing styles.
#[derive(Debug, PartialEq, Clone)]
pub(crate) struct Case(convert_case::Case);

// This is used to check if the first string is "case" and then attempt conversion of the second string.
Expand Down Expand Up @@ -44,6 +47,31 @@ impl TryFrom<String> for Case {
}
}

impl Display for Case {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let v = match self.0 {
convert_case::Case::Upper => "upper".to_string(),
convert_case::Case::Lower => "lower".to_string(),
convert_case::Case::Title => "title".to_string(),
convert_case::Case::Toggle => "toggle".to_string(),
convert_case::Case::Camel => "camel".to_string(),
convert_case::Case::Pascal => "pascal".to_string(),
convert_case::Case::UpperCamel => "upper_camel".to_string(),
convert_case::Case::Snake => "snake".to_string(),
convert_case::Case::UpperSnake => "upper_snake".to_string(),
convert_case::Case::ScreamingSnake => "screaming_snake".to_string(),
convert_case::Case::Kebab => "kebab".to_string(),
convert_case::Case::Cobol => "cobol".to_string(),
convert_case::Case::UpperKebab => "upper_kebab".to_string(),
convert_case::Case::Train => "train".to_string(),
convert_case::Case::Flat => "flat".to_string(),
convert_case::Case::UpperFlat => "upper_flat".to_string(),
convert_case::Case::Alternating => "alternating".to_string(),
};
write!(f, "{v}")
}
}

impl Case {
/// Applies the stored casing style to the given string `s` and returns the formatted result.
pub(crate) fn to_case(&self, s: &str) -> String {
Expand Down