Skip to content

Commit ba17114

Browse files
authored
feat(strcase): add functions (scaleway#275)
* feat: Add strcase functions
1 parent d7f2fd7 commit ba17114

File tree

10 files changed

+255
-0
lines changed

10 files changed

+255
-0
lines changed

strcase/camel.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package strcase
2+
3+
import "strings"
4+
5+
func ToCamel(s string) string {
6+
if s == "" {
7+
return s
8+
}
9+
if r := rune(s[0]); r >= 'A' && r <= 'Z' {
10+
s = strings.ToLower(string(r)) + s[1:]
11+
}
12+
return toPascalInitCase(s, false)
13+
}

strcase/camel_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package strcase
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestToCamel(t *testing.T) {
8+
cases := [][]string{
9+
{"foo-bar", "fooBar"},
10+
{"TestCase", "testCase"},
11+
{"", ""},
12+
{"AnyKind of_string", "anyKindOfString"},
13+
}
14+
for _, i := range cases {
15+
in := i[0]
16+
out := i[1]
17+
result := ToCamel(in)
18+
if result != out {
19+
t.Error("'" + result + "' != '" + out + "'")
20+
}
21+
}
22+
}

strcase/goname_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,24 @@ func TestLowerCaseFirstLetterOrAcronyms(t *testing.T) {
2525
}
2626
}
2727
}
28+
29+
func TestTitleFirstWord(t *testing.T) {
30+
cases := [][]string{
31+
{"", ""},
32+
{"t", "T"},
33+
{"Test Case", "Test Case"},
34+
{"test Case", "Test Case"},
35+
{"test case", "Test case"},
36+
{"TEST CASE", "TEST CASE"},
37+
{"tEST CASE", "TEST CASE"},
38+
{"#EST CASE", "#EST CASE"},
39+
}
40+
for _, i := range cases {
41+
in := i[0]
42+
out := i[1]
43+
result := TitleFirstWord(in)
44+
if result != out {
45+
t.Error("'" + result + "' != '" + out + "'")
46+
}
47+
}
48+
}

strcase/kebab.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package strcase
2+
3+
import "strings"
4+
5+
// Converts a string to kebab-case
6+
func ToKebab(s string) string {
7+
return strings.Replace(ToSnake(s), "_", "-", -1)
8+
}
9+
10+
// Converts a string to kebab-case
11+
func ToSpace(s string) string {
12+
return strings.Replace(ToSnake(s), "_", " ", -1)
13+
}

strcase/kebab_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package strcase
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestToKebab(t *testing.T) {
8+
cases := [][]string{
9+
{"testCase", "test-case"},
10+
{"TestCase", "test-case"},
11+
{"Test Case", "test-case"},
12+
{" Test Case", "test-case"},
13+
{"Test Case ", "test-case"},
14+
{" Test Case ", "test-case"},
15+
{"test", "test"},
16+
{"test_case", "test-case"},
17+
{"Test", "test"},
18+
{"", ""},
19+
{"ManyManyWords", "many-many-words"},
20+
{"manyManyWords", "many-many-words"},
21+
{"AnyKind of_string", "any-kind-of-string"},
22+
{"numbers2and55with000", "numbers2and55with000"},
23+
{"JSONData", "json-data"},
24+
{"userID", "user-id"},
25+
{"AAAbbb", "aa-abbb"},
26+
}
27+
for _, i := range cases {
28+
in := i[0]
29+
out := i[1]
30+
result := ToKebab(in)
31+
if result != out {
32+
t.Error("'" + result + "' != '" + out + "'")
33+
}
34+
}
35+
}

strcase/number.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package strcase
2+
3+
import (
4+
"regexp"
5+
)
6+
7+
var numberSequence = regexp.MustCompile(`([a-zA-Z])(\d+)([a-zA-Z]?)`)
8+
var numberReplacement = []byte(`$1 $2 $3`)
9+
10+
func addWordBoundariesToNumbers(s string) string {
11+
b := []byte(s)
12+
b = numberSequence.ReplaceAll(b, numberReplacement)
13+
return string(b)
14+
}

strcase/pascal.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package strcase
2+
3+
import "strings"
4+
5+
// Converts a string to CamelCase
6+
func toPascalInitCase(s string, initCase bool) string {
7+
s = addWordBoundariesToNumbers(s)
8+
s = strings.Trim(s, " ")
9+
n := ""
10+
capNext := initCase
11+
for _, v := range s {
12+
if v >= 'A' && v <= 'Z' {
13+
n += string(v)
14+
}
15+
if v >= '0' && v <= '9' {
16+
n += string(v)
17+
}
18+
if v >= 'a' && v <= 'z' {
19+
if capNext {
20+
n += strings.ToUpper(string(v))
21+
} else {
22+
n += string(v)
23+
}
24+
}
25+
if v == '_' || v == ' ' || v == '-' {
26+
capNext = true
27+
} else {
28+
capNext = false
29+
}
30+
}
31+
return n
32+
}
33+
34+
func ToPascal(s string) string {
35+
return toPascalInitCase(s, true)
36+
}

strcase/pascal_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package strcase
2+
3+
import "testing"
4+
5+
func TestToPascal(t *testing.T) {
6+
cases := [][]string{
7+
{"test_case", "TestCase"},
8+
{"test", "Test"},
9+
{"TestCase", "TestCase"},
10+
{" test case ", "TestCase"},
11+
{"", ""},
12+
{"many_many_words", "ManyManyWords"},
13+
{"AnyKind of_string", "AnyKindOfString"},
14+
{"odd-fix", "OddFix"},
15+
{"numbers2And55with000", "Numbers2And55With000"},
16+
}
17+
for _, i := range cases {
18+
in := i[0]
19+
out := i[1]
20+
result := ToPascal(in)
21+
if result != out {
22+
t.Error("'" + result + "' != '" + out + "'")
23+
}
24+
}
25+
}

strcase/snake.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package strcase
2+
3+
import (
4+
"strings"
5+
)
6+
7+
// Converts a string to snake_case
8+
func ToSnake(s string) string {
9+
s = strings.Trim(s, " ")
10+
n := ""
11+
for i, v := range s {
12+
// treat acronyms as words, eg for JSONData -> JSON is a whole word
13+
nextCaseIsChanged := false
14+
if i+1 < len(s) {
15+
next := s[i+1]
16+
if (isUpperLetter(v) && isLowerLetter(int32(next))) || (isLowerLetter(v) && isUpperLetter(int32(next))) {
17+
nextCaseIsChanged = true
18+
}
19+
}
20+
21+
if i > 0 && n[len(n)-1] != '_' && nextCaseIsChanged {
22+
// add underscore if next letter case type is changed
23+
if isUpperLetter(v) {
24+
n += "_" + string(v)
25+
} else if isLowerLetter(v) {
26+
n += string(v) + "_"
27+
}
28+
} else if v == ' ' || v == '-' {
29+
// replace spaces and dashes with underscores
30+
n += "_"
31+
} else {
32+
n = n + string(v)
33+
}
34+
}
35+
n = strings.ToLower(n)
36+
return n
37+
}

strcase/snake_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package strcase
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestToSnake(t *testing.T) {
8+
cases := [][2]string{
9+
{"testCase", "test_case"},
10+
{"TestCase", "test_case"},
11+
{"Test Case", "test_case"},
12+
{" Test Case", "test_case"},
13+
{"Test Case ", "test_case"},
14+
{" Test Case ", "test_case"},
15+
{"test", "test"},
16+
{"test_case", "test_case"},
17+
{"Test", "test"},
18+
{"", ""},
19+
{"ManyManyWords", "many_many_words"},
20+
{"manyManyWords", "many_many_words"},
21+
{"AnyKind of_string", "any_kind_of_string"},
22+
{"numbers2and55with000", "numbers2and55with000"},
23+
{"ip-v6", "ip_v6"},
24+
{"ipV6", "ip_v6"},
25+
{"IPV6", "ipv6"},
26+
{"ipv6", "ipv6"},
27+
{"JSONData", "json_data"},
28+
{"userID", "user_id"},
29+
{"AAAbbb", "aa_abbb"},
30+
}
31+
for _, i := range cases {
32+
in := i[0]
33+
out := i[1]
34+
result := ToSnake(in)
35+
if result != out {
36+
t.Error("'" + result + "' != '" + out + "'")
37+
}
38+
}
39+
}

0 commit comments

Comments
 (0)