From c5e2d6363d71c1f11aff38c1797f072105df4cce Mon Sep 17 00:00:00 2001 From: Pascal Bach Date: Thu, 11 Jul 2024 21:52:47 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20zv:=20support=20kebab-case=20in=20r?= =?UTF-8?q?ename=5Fall?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also add some tests for case conversion to ensure renamings are consistent Signed-off-by: Pascal Bach --- zbus_macros/src/iface.rs | 2 +- zvariant_derive/src/dict.rs | 3 +- zvariant_derive/src/lib.rs | 2 ++ zvariant_utils/src/case.rs | 70 +++++++++++++++++++++++++++++++------ 4 files changed, 65 insertions(+), 12 deletions(-) diff --git a/zbus_macros/src/iface.rs b/zbus_macros/src/iface.rs index 51112ab4f..a961aa572 100644 --- a/zbus_macros/src/iface.rs +++ b/zbus_macros/src/iface.rs @@ -515,7 +515,7 @@ pub fn expand, M: AttrParse + Into>( "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"); diff --git a/zvariant_derive/src/dict.rs b/zvariant_derive/src/dict.rs index a0671e1c4..17de0100f 100644 --- a/zvariant_derive/src/dict.rs +++ b/zvariant_derive/src/dict.rs @@ -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(), diff --git a/zvariant_derive/src/lib.rs b/zvariant_derive/src/lib.rs index decfd1dbd..31531831e 100644 --- a/zvariant_derive/src/lib.rs +++ b/zvariant_derive/src/lib.rs @@ -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))] @@ -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))] diff --git a/zvariant_utils/src/case.rs b/zvariant_utils/src/case.rs index ac0755f07..96721b090 100644 --- a/zvariant_utils/src/case.rs +++ b/zvariant_utils/src/case.rs @@ -1,6 +1,6 @@ //! 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 { @@ -8,7 +8,7 @@ pub fn pascal_or_camel_case(s: &str, is_pascal_case: bool) -> String { 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()); @@ -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)); + } }