Skip to content

Commit

Permalink
fix #41. add new method ToKebabCase
Browse files Browse the repository at this point in the history
  • Loading branch information
huandu committed Sep 6, 2018
1 parent 55ae428 commit f02667b
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 18 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) |
Expand Down
51 changes: 36 additions & 15 deletions convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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 ""
}
Expand All @@ -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
Expand All @@ -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))
Expand All @@ -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.
Expand All @@ -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)
}
Expand All @@ -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)
Expand Down
25 changes: 22 additions & 3 deletions convert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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) {
Expand All @@ -42,13 +53,17 @@ func TestToCamelCase(t *testing.T) {
"all": "All",
"GOLANG_IS_GREAT": "GolangIsGreat",
"GOLANG": "Golang",

"": "",
})
}

func TestSwapCase(t *testing.T) {
runTestCases(t, SwapCase, _M{
"swapCase": "SWAPcASE",
"Θ~λa云Ξπ": "θ~ΛA云ξΠ",

"": "",
})
}

Expand All @@ -57,6 +72,8 @@ func TestFirstRuneToUpper(t *testing.T) {
"hello, world!": "Hello, world!",
"Hello, world!": "Hello, world!",
"你好,世界!": "你好,世界!",

"": "",
})
}

Expand All @@ -65,6 +82,8 @@ func TestFirstRuneToLower(t *testing.T) {
"hello, world!": "hello, world!",
"Hello, world!": "hello, world!",
"你好,世界!": "你好,世界!",

"": "",
})
}

Expand Down

0 comments on commit f02667b

Please sign in to comment.