From 12b1187b56e958a020ce416482715d8b04ab8909 Mon Sep 17 00:00:00 2001 From: Jared Petersen Date: Sun, 15 May 2022 01:40:20 -0600 Subject: [PATCH] support initialisms when naming --- pkg/codegen/utils.go | 59 ++++++++++++++++++++++----------------- pkg/codegen/utils_test.go | 34 ++++++++++++++++++---- 2 files changed, 62 insertions(+), 31 deletions(-) diff --git a/pkg/codegen/utils.go b/pkg/codegen/utils.go index a7dc3cf710..31aedb14fa 100644 --- a/pkg/codegen/utils.go +++ b/pkg/codegen/utils.go @@ -57,33 +57,40 @@ func LowercaseFirstCharacter(str string) string { // So, "word.word-word+word:word;word_word~word word(word)word{word}[word]" // would be converted to WordWordWordWordWordWordWordWordWordWordWordWordWord func ToCamelCase(str string) string { - separators := "-#@!$&=.+:;_~ (){}[]" + specialCharRegexStr := "[-#@!$&=.+:;_~ (){}\\[\\]]" + + // Support initialisms + // https://github.com/golang/go/wiki/CodeReviewComments#initialisms + // https://staticcheck.io/docs/configuration/options/#initialisms + acronyms := []string{"ACL", "API", "ASCII", "CPU", "CSS", "DNS", "EOF", + "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "QPS", "RAM", + "RPC", "SLA", "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", + "GID", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XMPP", + "XSRF", "XSS", "SIP", "RTP", "AMQP", "DB", "TS"} + + acronymsSepRegexStr := fmt.Sprintf("($|%s.)|", specialCharRegexStr) + acronymsInitialRegexStr := strings.Join(acronyms, acronymsSepRegexStr) + + acronymsSepRegexStr + acronymsTrailingRegexStr := strings.Join(acronyms, acronymsSepRegexStr) + + acronymsSepRegexStr + + camelCaseRegexStr := fmt.Sprintf("(?i)^(%s[A-Za-z0-9])|%s(%s[A-Za-z0-9])", + acronymsInitialRegexStr, + specialCharRegexStr, + acronymsTrailingRegexStr) + + camelCaseRegex := regexp.MustCompile(camelCaseRegexStr) + specialCharRegex := regexp.MustCompile(specialCharRegexStr) + + // If you wanted true camelCase without user overriding you could + // set the string to lowercase beforehand + // However, users may expect some degree of override already, and we don't + // want to break backwards compatibility s := strings.Trim(str, " ") - - n := "" - capNext := true - for _, v := range s { - if unicode.IsUpper(v) { - n += string(v) - } - if unicode.IsDigit(v) { - n += string(v) - } - if unicode.IsLower(v) { - if capNext { - n += strings.ToUpper(string(v)) - } else { - n += string(v) - } - } - - if strings.ContainsRune(separators, v) { - capNext = true - } else { - capNext = false - } - } - return n + s = camelCaseRegex.ReplaceAllStringFunc(s, func(s string) string { + return strings.ToUpper(s) + }) + return specialCharRegex.ReplaceAllString(s, "") } // This function returns the keys of the given SchemaRef dictionary in sorted diff --git a/pkg/codegen/utils_test.go b/pkg/codegen/utils_test.go index b391852a99..004b784367 100644 --- a/pkg/codegen/utils_test.go +++ b/pkg/codegen/utils_test.go @@ -20,12 +20,36 @@ import ( "github.com/stretchr/testify/assert" ) -func TestStringOps(t *testing.T) { - // Test that each substitution works - assert.Equal(t, "WordWordWORDWordWordWordWordWordWordWordWordWordWord", ToCamelCase("word.word-WORD+Word_word~word(Word)Word{Word}Word[Word]Word:Word;"), "Camel case conversion failed") +func TestCamelCase(t *testing.T) { + type test struct { + input string + expected string + } - // Make sure numbers don't interact in a funny way. - assert.Equal(t, "Number1234", ToCamelCase("number-1234"), "Number Camelcasing not working.") + tests := []test{ + { + input: "word.word-WORD+Word_word~word(Word)Word{Word}Word[Word]Word:Word;", + expected: "WordWordWORDWordWordWordWordWordWordWordWordWordWord", + }, + {input: "number-1234", expected: "Number1234"}, + {input: "Foo Bar", expected: "FooBar"}, + {input: "db_user", expected: "DBUser"}, + {input: "Db_uSer", expected: "DBUSer"}, + {input: "user_db", expected: "UserDB"}, + {input: "USer_dB", expected: "USerDB"}, + {input: "user_db_pasta", expected: "UserDBPasta"}, + {input: "uSeR_Db_PASta", expected: "USeRDBPASta"}, + {input: "user_id_pasta", expected: "UserIDPasta"}, + {input: "User_iD_paSTA", expected: "UserIDPaSTA"}, + {input: "user_ident_pasta", expected: "UserIdentPasta"}, + {input: "usEr_iDENT_pAsta", expected: "UsErIDENTPAsta"}, + {input: "identifier", expected: "Identifier"}, + {input: "identifier_pasta", expected: "IdentifierPasta"}, + } + + for _, tc := range tests { + assert.Equal(t, tc.expected, ToCamelCase(tc.input), "Incorrect CamelCase conversion") + } } func TestSortedSchemaKeys(t *testing.T) {