diff --git a/README.md b/README.md index 866287b..0e8635d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![CircleCI](https://circleci.com/gh/stoewer/go-strcase/tree/master.svg?style=svg)](https://circleci.com/gh/stoewer/go-strcase/tree/master) [![codecov](https://codecov.io/gh/stoewer/go-strcase/branch/master/graph/badge.svg)](https://codecov.io/gh/stoewer/go-strcase) -[![GoDoc](https://godoc.org/github.com/stoewer/go-strcase?status.svg)](https://godoc.org/github.com/stoewer/go-strcase) +[![GoDoc](https://godoc.org/github.com/stoewer/go-strcase?status.svg)](https://pkg.go.dev/github.com/stoewer/go-strcase) --- Go strcase diff --git a/go.mod b/go.mod index 6793c46..8a360ab 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,4 @@ module github.com/stoewer/go-strcase go 1.11 -require github.com/stretchr/testify v1.4.0 +require github.com/stretchr/testify v1.5.1 diff --git a/go.sum b/go.sum index 8fdee58..331fa69 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= diff --git a/kebab.go b/kebab.go index 87467fd..e9a6487 100644 --- a/kebab.go +++ b/kebab.go @@ -5,5 +5,10 @@ package strcase // KebabCase converts a string into kebab case. func KebabCase(s string) string { - return lowerDelimiterCase(s, '-') + return delimiterCase(s, '-', false) +} + +// UpperKebabCase converts a string into kebab case with capital letters. +func UpperKebabCase(s string) string { + return delimiterCase(s, '-', true) } diff --git a/kebab_test.go b/kebab_test.go index 07aeefa..d7789b5 100644 --- a/kebab_test.go +++ b/kebab_test.go @@ -31,3 +31,26 @@ func TestKebabCase(t *testing.T) { assert.Equal(t, snake, converted) } } + +func TestUpperKebabCase(t *testing.T) { + data := map[string]string{ + "": "", + "F": "F", + "Foo": "FOO", + "FooB": "FOO-B", + "FooID": "FOO-ID", + " FooBar\t": "FOO-BAR", + "HTTPStatusCode": "HTTP-STATUS-CODE", + "ParseURL.DoParse": "PARSE-URL.DO-PARSE", + "Convert Space": "CONVERT-SPACE", + "Convert-dash": "CONVERT-DASH", + "Skip___MultipleUnderscores": "SKIP-MULTIPLE-UNDERSCORES", + "Skip MultipleSpaces": "SKIP-MULTIPLE-SPACES", + "Skip---MultipleDashes": "SKIP-MULTIPLE-DASHES", + } + + for camel, snake := range data { + converted := UpperKebabCase(camel) + assert.Equal(t, snake, converted) + } +} diff --git a/snake.go b/snake.go index 417f181..1b216e2 100644 --- a/snake.go +++ b/snake.go @@ -9,15 +9,25 @@ import ( // SnakeCase converts a string into snake case. func SnakeCase(s string) string { - return lowerDelimiterCase(s, '_') + return delimiterCase(s, '_', false) } -// lowerDelimiterCase converts a string into snake_case or kebab-case depending on -// the delimiter passed in as second argument. -func lowerDelimiterCase(s string, delimiter rune) string { +// UpperSnakeCase converts a string into snake case with capital letters. +func UpperSnakeCase(s string) string { + return delimiterCase(s, '_', true) +} + +// delimiterCase converts a string into snake_case or kebab-case depending on the delimiter passed +// as second argument. When upperCase is true the result will be UPPER_SNAKE_CASE or UPPER-KEBAB-CASE. +func delimiterCase(s string, delimiter rune, upperCase bool) string { s = strings.TrimSpace(s) buffer := make([]rune, 0, len(s)+3) + adjustCase := toLower + if upperCase { + adjustCase = toUpper + } + var prev rune var curr rune for _, next := range s { @@ -29,9 +39,9 @@ func lowerDelimiterCase(s string, delimiter rune) string { if isLower(prev) || (isUpper(prev) && isLower(next)) { buffer = append(buffer, delimiter) } - buffer = append(buffer, toLower(curr)) + buffer = append(buffer, adjustCase(curr)) } else if curr != 0 { - buffer = append(buffer, curr) + buffer = append(buffer, adjustCase(curr)) } prev = curr curr = next @@ -41,7 +51,7 @@ func lowerDelimiterCase(s string, delimiter rune) string { if isUpper(curr) && isLower(prev) && prev != 0 { buffer = append(buffer, delimiter) } - buffer = append(buffer, toLower(curr)) + buffer = append(buffer, adjustCase(curr)) } return string(buffer) diff --git a/snake_test.go b/snake_test.go index 6064dbd..f04b74a 100644 --- a/snake_test.go +++ b/snake_test.go @@ -31,3 +31,26 @@ func TestSnakeCase(t *testing.T) { assert.Equal(t, snake, converted) } } + +func TestUpperSnakeCase(t *testing.T) { + data := map[string]string{ + "": "", + "F": "F", + "Foo": "FOO", + "FooB": "FOO_B", + "FooID": "FOO_ID", + " FooBar\t": "FOO_BAR", + "HTTPStatusCode": "HTTP_STATUS_CODE", + "ParseURL.DoParse": "PARSE_URL.DO_PARSE", + "Convert Space": "CONVERT_SPACE", + "Convert-dash": "CONVERT_DASH", + "Skip___MultipleUnderscores": "SKIP_MULTIPLE_UNDERSCORES", + "Skip MultipleSpaces": "SKIP_MULTIPLE_SPACES", + "Skip---MultipleDashes": "SKIP_MULTIPLE_DASHES", + } + + for camel, snake := range data { + converted := UpperSnakeCase(camel) + assert.Equal(t, snake, converted) + } +}