diff --git a/libs/textutil/case.go b/libs/textutil/case.go index 2b655b78cb9..dbaef9e14bd 100644 --- a/libs/textutil/case.go +++ b/libs/textutil/case.go @@ -2,11 +2,19 @@ package textutil import "unicode" +// CamelToSnakeCase converts camelCase or PascalCase to snake_case. A run of +// capitals is kept together as one word, so "HTTPServer" becomes "http_server" +// rather than "h_t_t_p_server". func CamelToSnakeCase(name string) string { - out := make([]rune, 0, len(name)*2) - for i, r := range name { + runes := []rune(name) + out := make([]rune, 0, len(runes)*2) + for i, r := range runes { if i > 0 && unicode.IsUpper(r) { - out = append(out, '_') + prev := runes[i-1] + nextIsLower := i+1 < len(runes) && unicode.IsLower(runes[i+1]) + if unicode.IsLower(prev) || unicode.IsDigit(prev) || (unicode.IsUpper(prev) && nextIsLower) { + out = append(out, '_') + } } out = append(out, unicode.ToLower(r)) } diff --git a/libs/textutil/case_test.go b/libs/textutil/case_test.go index 77b3e067994..81961a1bc81 100644 --- a/libs/textutil/case_test.go +++ b/libs/textutil/case_test.go @@ -31,6 +31,22 @@ func TestCamelToSnakeCase(t *testing.T) { input: "TestTestTest", expected: "test_test_test", }, + { + input: "HTTPServer", + expected: "http_server", + }, + { + input: "userID", + expected: "user_id", + }, + { + input: "getHTTPResponseCode", + expected: "get_http_response_code", + }, + { + input: "version2Beta", + expected: "version2_beta", + }, } for _, c := range cases {