From f02667b379e2fb5916c3cda2cf31e0eb885d79f8 Mon Sep 17 00:00:00 2001 From: Huan Du Date: Thu, 6 Sep 2018 09:50:35 -0500 Subject: [PATCH] fix #41. add new method ToKebabCase --- README.md | 1 + convert.go | 51 ++++++++++++++++++++++++++++++++++--------------- convert_test.go | 25 +++++++++++++++++++++--- 3 files changed, 59 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index c824a5c..a4a36f4 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ Here is a list of functions in [strings](http://golang.org/pkg/strings) and [xst | [Successor](https://godoc.org/github.com/huandu/xstrings#Successor) | `String#succ` or `String#next` in Ruby | [#22](https://github.com/huandu/xstrings/issues/22) | | [SwapCase](https://godoc.org/github.com/huandu/xstrings#SwapCase) | `str.swapcase` in Python; `String#swapcase` in Ruby | [#12](https://github.com/huandu/xstrings/issues/12) | | [ToCamelCase](https://godoc.org/github.com/huandu/xstrings#ToCamelCase) | `String#camelize` in RoR | [#1](https://github.com/huandu/xstrings/issues/1) | +| [ToKebab](https://godoc.org/github.com/huandu/xstrings#ToKebabCase) | - | [#41](https://github.com/huandu/xstrings/issues/41) | | [ToSnakeCase](https://godoc.org/github.com/huandu/xstrings#ToSnakeCase) | `String#underscore` in RoR | [#1](https://github.com/huandu/xstrings/issues/1) | | [Translate](https://godoc.org/github.com/huandu/xstrings#Translate) | `str.translate` in Python; `String#tr` in Ruby; `strtr` in PHP; `tr///` in Perl | [#21](https://github.com/huandu/xstrings/issues/21) | | [Width](https://godoc.org/github.com/huandu/xstrings#Width) | `mb_strwidth` in PHP | [#26](https://github.com/huandu/xstrings/issues/26) | diff --git a/convert.go b/convert.go index 3399a05..8253fa9 100644 --- a/convert.go +++ b/convert.go @@ -72,7 +72,7 @@ func ToCamelCase(str string) string { } // ToSnakeCase can convert all upper case characters in a string to -// underscore format. +// snake case format. // // Some samples. // "FirstName" => "first_name" @@ -85,6 +85,27 @@ func ToCamelCase(str string) string { // "http2xx" => "http_2xx" // "HTTP20xOK" => "http_20x_ok" func ToSnakeCase(str string) string { + return camelCaseToLowerCase(str, '_') +} + +// ToKebabCase can convert all upper case characters in a string to +// kebab case format. +// +// Some samples. +// "FirstName" => "first-name" +// "HTTPServer" => "http-server" +// "NoHTTPS" => "no-https" +// "GO_PATH" => "go-path" +// "GO PATH" => "go-path" // space is converted to '-'. +// "GO-PATH" => "go-path" // hyphen is converted to '-'. +// "HTTP2XX" => "http-2xx" // insert a '-' before a number and after an alphabet. +// "http2xx" => "http-2xx" +// "HTTP20xOK" => "http-20x-ok" +func ToKebabCase(str string) string { + return camelCaseToLowerCase(str, '-') +} + +func camelCaseToLowerCase(str string, connector rune) string { if len(str) == 0 { return "" } @@ -93,7 +114,7 @@ func ToSnakeCase(str string) string { var prev, r0, r1 rune var size int - r0 = '_' + r0 = connector for len(str) > 0 { prev = r0 @@ -102,11 +123,11 @@ func ToSnakeCase(str string) string { switch { case r0 == utf8.RuneError: - buf.WriteByte(byte(str[0])) + buf.WriteRune(r0) case unicode.IsUpper(r0): - if prev != '_' && !unicode.IsNumber(prev) { - buf.WriteRune('_') + if prev != connector && !unicode.IsNumber(prev) { + buf.WriteRune(connector) } buf.WriteRune(unicode.ToLower(r0)) @@ -123,7 +144,7 @@ func ToSnakeCase(str string) string { break } - // find next non-upper-case character and insert `_` properly. + // find next non-upper-case character and insert connector properly. // it's designed to convert `HTTPServer` to `http_server`. // if there are more than 2 adjacent upper case characters in a word, // treat them as an abbreviation plus a normal word. @@ -134,23 +155,23 @@ func ToSnakeCase(str string) string { if r0 == utf8.RuneError { buf.WriteRune(unicode.ToLower(r1)) - buf.WriteByte(byte(str[0])) + buf.WriteRune(r0) break } if !unicode.IsUpper(r0) { if r0 == '_' || r0 == ' ' || r0 == '-' { - r0 = '_' + r0 = connector buf.WriteRune(unicode.ToLower(r1)) } else if unicode.IsNumber(r0) { // treat a number as an upper case rune // so that both `http2xx` and `HTTP2XX` can be converted to `http_2xx`. buf.WriteRune(unicode.ToLower(r1)) - buf.WriteRune('_') + buf.WriteRune(connector) buf.WriteRune(r0) } else { - buf.WriteRune('_') + buf.WriteRune(connector) buf.WriteRune(unicode.ToLower(r1)) buf.WriteRune(r0) } @@ -161,20 +182,20 @@ func ToSnakeCase(str string) string { buf.WriteRune(unicode.ToLower(r1)) } - if len(str) == 0 || r0 == '_' { + if len(str) == 0 || r0 == connector { buf.WriteRune(unicode.ToLower(r0)) } case unicode.IsNumber(r0): - if prev != '_' && !unicode.IsNumber(prev) { - buf.WriteRune('_') + if prev != connector && !unicode.IsNumber(prev) { + buf.WriteRune(connector) } buf.WriteRune(r0) default: - if r0 == ' ' || r0 == '-' { - r0 = '_' + if r0 == ' ' || r0 == '-' || r0 == '_' { + r0 = connector } buf.WriteRune(r0) diff --git a/convert_test.go b/convert_test.go index 5c10f97..c80e9b7 100644 --- a/convert_test.go +++ b/convert_test.go @@ -9,8 +9,8 @@ import ( "testing" ) -func TestToSnakeCase(t *testing.T) { - runTestCases(t, ToSnakeCase, _M{ +func TestToSnakeCaseAndToKebabCase(t *testing.T) { + cases := _M{ "HTTPServer": "http_server", "_camelCase": "_camel_case", "NoHTTPS": "no_https", @@ -30,7 +30,18 @@ func TestToSnakeCase(t *testing.T) { " sentence case ": "__sentence_case__", " Mixed-hyphen case _and SENTENCE_case and UPPER-case": "_mixed_hyphen_case__and_sentence_case_and_upper_case", - }) + + "": "", + "Abc\uFFFDE\uFFFDf\uFFFDd\uFFFD2\uFFFD00Z\uFFFDZZ\uFFFDZZ": "abc\uFFFD_e\uFFFDf\uFFFDd\uFFFD_2\uFFFD_00z\uFFFD_zz\uFFFD_zz", + } + + runTestCases(t, ToSnakeCase, cases) + + for k, v := range cases { + cases[k] = strings.Replace(v, "_", "-", -1) + } + + runTestCases(t, ToKebabCase, cases) } func TestToCamelCase(t *testing.T) { @@ -42,6 +53,8 @@ func TestToCamelCase(t *testing.T) { "all": "All", "GOLANG_IS_GREAT": "GolangIsGreat", "GOLANG": "Golang", + + "": "", }) } @@ -49,6 +62,8 @@ func TestSwapCase(t *testing.T) { runTestCases(t, SwapCase, _M{ "swapCase": "SWAPcASE", "Θ~λa云Ξπ": "θ~ΛA云ξΠ", + + "": "", }) } @@ -57,6 +72,8 @@ func TestFirstRuneToUpper(t *testing.T) { "hello, world!": "Hello, world!", "Hello, world!": "Hello, world!", "你好,世界!": "你好,世界!", + + "": "", }) } @@ -65,6 +82,8 @@ func TestFirstRuneToLower(t *testing.T) { "hello, world!": "hello, world!", "Hello, world!": "hello, world!", "你好,世界!": "你好,世界!", + + "": "", }) }