diff --git a/vlib/builtin/string.v b/vlib/builtin/string.v index b2fa182301..8975f3d82a 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 50ec55fa8e..09661a69f1 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' +}