From 520a2609d10e58c20c60c82176fd9a22cd70d28b Mon Sep 17 00:00:00 2001 From: kbkpbot Date: Sun, 16 Jun 2024 02:02:44 +0800 Subject: [PATCH] builtin: add string.camel_to_snake/0 and string.snake_to_camel/0 methods (#21679) --- vlib/builtin/string.v | 94 ++++++++++++++++++++++++++++++++++++++ vlib/builtin/string_test.v | 14 ++++++ 2 files changed, 108 insertions(+) diff --git a/vlib/builtin/string.v b/vlib/builtin/string.v index b2fa1823011714..8975f3d82ab5f9 100644 --- a/vlib/builtin/string.v +++ b/vlib/builtin/string.v @@ -2606,3 +2606,97 @@ pub fn (name string) match_glob(pattern string) bool { pub fn (s string) is_ascii() bool { return !s.bytes().any(it < u8(` `) || it > u8(`~`)) } + +// camel_to_snake convert string from camelCase to snake_case +// Example: assert 'Abcd'.camel_to_snake() == 'abcd' +// Example: assert 'aaBB'.camel_to_snake() == 'aa_bb' +// Example: assert 'BBaa'.camel_to_snake() == 'b_baa' +// Example: assert 'aa_BB'.camel_to_snake() == 'aa_bb' +@[direct_array_access] +pub fn (s string) camel_to_snake() string { + if s.len == 0 { + return '' + } + mut prev_is_upper := false + mut prev_char := ` ` + mut lower_c := `_` + mut c_is_upper := false + mut b := unsafe { malloc_noscan(2 * s.len + 1) } + mut i := 0 + for c in s { + c_is_upper = c >= `A` && c <= `Z` + lower_c = if c_is_upper { c + 32 } else { c } + if i > 0 { + if prev_is_upper == false && c_is_upper { + // aB => a_b, if prev has `_`, then do not add `_` + unsafe { + if b[i - 1] != `_` { + b[i] = `_` + i++ + } + } + } else if prev_is_upper && c_is_upper == false && c != `_` { + // Ba => _ba, if prev has `_`, then do not add `_` + if i > 1 { + unsafe { + if b[i - 2] != `_` { + prev_char = b[i - 1] + b[i - 1] = `_` + b[i] = prev_char + i++ + } + } + } + } + } + unsafe { + b[i] = lower_c + } + prev_is_upper = c_is_upper + i++ + } + unsafe { + b[i] = 0 + } + return unsafe { tos(b, i) } +} + +// snake_to_camel convert string from snake_case to camelCase +// Example: assert 'abcd'.snake_to_camel() == 'Abcd' +// Example: assert 'ab_cd'.snake_to_camel() == 'AbCd' +// Example: assert '_abcd'.snake_to_camel() == 'Abcd' +// Example: assert '_abcd_'.snake_to_camel() == 'Abcd' +@[direct_array_access] +pub fn (s string) snake_to_camel() string { + if s.len == 0 { + return '' + } + if s.len == 1 { + return s + } + mut need_upper := true + mut upper_c := `_` + mut b := unsafe { malloc_noscan(s.len + 1) } + mut i := 0 + for c in s { + upper_c = if c >= `a` && c <= `z` { c - 32 } else { c } + if c == `_` { + need_upper = true + } else if need_upper { + unsafe { + b[i] = upper_c + } + i++ + need_upper = false + } else { + unsafe { + b[i] = c + } + i++ + } + } + unsafe { + b[i] = 0 + } + return unsafe { tos(b, i) } +} diff --git a/vlib/builtin/string_test.v b/vlib/builtin/string_test.v index 50ec55fa8eec08..09661a69f18a52 100644 --- a/vlib/builtin/string_test.v +++ b/vlib/builtin/string_test.v @@ -1521,3 +1521,17 @@ fn test_contains_byte() { assert 'abc abca'.contains_u8(` `) assert !'abc abca'.contains_u8(`A`) } + +fn test_camel_to_snake() { + assert 'Abcd'.camel_to_snake() == 'abcd' + assert 'aaBB'.camel_to_snake() == 'aa_bb' + assert 'BBaa'.camel_to_snake() == 'b_baa' + assert 'aa_BB'.camel_to_snake() == 'aa_bb' +} + +fn test_snake_to_camel() { + assert 'abcd'.snake_to_camel() == 'Abcd' + assert 'ab_cd'.snake_to_camel() == 'AbCd' + assert '_abcd'.snake_to_camel() == 'Abcd' + assert '_abcd_'.snake_to_camel() == 'Abcd' +}