Skip to content

Commit

Permalink
Merge pull request #899 from bachp/zvariant_derive-kebab-case
Browse files Browse the repository at this point in the history
✨ zv: support kebab-case in rename_all
  • Loading branch information
zeenix authored Jul 16, 2024
2 parents 6c36c59 + c5e2d63 commit eeed9a6
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 12 deletions.
2 changes: 1 addition & 1 deletion zbus_macros/src/iface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ pub fn expand<T: AttrParse + Into<ImplAttrs>, M: AttrParse + Into<MethodAttrs>>(
"Write-only properties aren't supported yet",
))?;

let sk_member_name = case::snake_case(&member_name);
let sk_member_name = case::snake_or_kebab_case(&member_name, true);
let prop_changed_method_name = format_ident!("{sk_member_name}_changed");
let prop_invalidate_method_name = format_ident!("{sk_member_name}_invalidate");

Expand Down
3 changes: 2 additions & 1 deletion zvariant_derive/src/dict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ fn dict_name_for_field(
Some("UPPERCASE") => Ok(ident.to_ascii_uppercase()),
Some("PascalCase") => Ok(case::pascal_or_camel_case(&ident, true)),
Some("camelCase") => Ok(case::pascal_or_camel_case(&ident, false)),
Some("snake_case") => Ok(case::snake_case(&ident)),
Some("snake_case") => Ok(case::snake_or_kebab_case(&ident, true)),
Some("kebab-case") => Ok(case::snake_or_kebab_case(&ident, false)),
None => Ok(ident),
Some(other) => Err(Error::new(
f.span(),
Expand Down
2 changes: 2 additions & 0 deletions zvariant_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ pub fn type_macro_derive(input: TokenStream) -> TokenStream {
/// * `"PascalCase"`
/// * `"camelCase"`
/// * `"snake_case"`
/// * `"kebab-case"`
///
/// [`Serialize`]: https://docs.serde.rs/serde/trait.Serialize.html
#[proc_macro_derive(SerializeDict, attributes(zvariant))]
Expand Down Expand Up @@ -292,6 +293,7 @@ pub fn serialize_dict_macro_derive(input: TokenStream) -> TokenStream {
/// * `"PascalCase"`
/// * `"camelCase"`
/// * `"snake_case"`
/// * `"kebab-case"`
///
/// [`Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
#[proc_macro_derive(DeserializeDict, attributes(zvariant))]
Expand Down
70 changes: 60 additions & 10 deletions zvariant_utils/src/case.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
//! Contains utilities used to convert strings between different cases.
/// Convert to pascal or camel case, assuming snake case.
/// Convert to pascal or camel case, assuming snake or kebab case.
///
/// If `s` is already in pascal or camel case, should yield the same result.
pub fn pascal_or_camel_case(s: &str, is_pascal_case: bool) -> String {
let mut result = String::new();
let mut capitalize = is_pascal_case;
let mut first = true;
for ch in s.chars() {
if ch == '_' {
if ch == '_' || ch == '-' {
capitalize = true;
} else if capitalize {
result.push(ch.to_ascii_uppercase());
Expand All @@ -26,16 +26,66 @@ pub fn pascal_or_camel_case(s: &str, is_pascal_case: bool) -> String {
result
}

/// Convert to snake case, assuming pascal case.
/// Convert to snake or kebab case, assuming camel or Pascal case.
///
/// If `s` is already in snake case, should yield the same result.
pub fn snake_case(s: &str) -> String {
let mut snake = String::new();
/// If `s` is already in snake or kebab case, should yield the same result.
pub fn snake_or_kebab_case(s: &str, is_snake_case: bool) -> String {
let mut result = String::new();
for ch in s.chars() {
if ch.is_ascii_uppercase() && !snake.is_empty() {
snake.push('_');
if ch.is_ascii_uppercase() && !result.is_empty() {
if is_snake_case {
result.push('_');
} else {
result.push('-');
}
};

if ch == '_' || ch == '-' {
if is_snake_case {
result.push('_');
} else {
result.push('-');
}
} else {
result.push(ch.to_ascii_lowercase());
}
snake.push(ch.to_ascii_lowercase());
}
snake
result
}

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

#[test]
fn test_pascal_case() {
assert_eq!("FooBar", pascal_or_camel_case("foo_bar", true));
assert_eq!("FooBar", pascal_or_camel_case("fooBar", true));
assert_eq!("FooBar", pascal_or_camel_case("foo-bar", true));
assert_eq!("FooBar", pascal_or_camel_case("FooBar", true));
}

#[test]
fn test_camel_case() {
assert_eq!("fooBar", pascal_or_camel_case("foo_bar", false));
assert_eq!("fooBar", pascal_or_camel_case("fooBar", false));
assert_eq!("fooBar", pascal_or_camel_case("foo-bar", false));
assert_eq!("fooBar", pascal_or_camel_case("FooBar", false));
}

#[test]
fn test_snake_case() {
assert_eq!("foo_bar", snake_or_kebab_case("foo_bar", true));
assert_eq!("foo_bar", snake_or_kebab_case("fooBar", true));
assert_eq!("foo_bar", snake_or_kebab_case("foo-bar", true));
assert_eq!("foo_bar", snake_or_kebab_case("FooBar", true));
}

#[test]
fn test_kebab_case() {
assert_eq!("foo-bar", snake_or_kebab_case("foo_bar", false));
assert_eq!("foo-bar", snake_or_kebab_case("fooBar", false));
assert_eq!("foo-bar", snake_or_kebab_case("foo-bar", false));
assert_eq!("foo-bar", snake_or_kebab_case("FooBar", false));
}
}

0 comments on commit eeed9a6

Please sign in to comment.