Skip to content

Commit 6edba47

Browse files
authored
feat: add *Case func to stringz package (#101)
2 parents 86030ef + 5ba4af6 commit 6edba47

File tree

2 files changed

+379
-0
lines changed

2 files changed

+379
-0
lines changed

strings/case.go

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
package stringz
2+
3+
import (
4+
"strings"
5+
)
6+
7+
type _Case struct {
8+
allUpper bool // [A-Z]+
9+
allLower bool // [a-z]+
10+
firstUpper bool // [A-Z][0-9a-z_-]+
11+
firstLower bool // [a-z][0-9a-z_-]+
12+
hasUpper bool // .*[A-Z].*
13+
hasLower bool // .*[a-z].*
14+
hasUnderscore bool // .*_.*
15+
hasDash bool // .*-.*
16+
hasNumber bool // .*[0-9].*
17+
hasOther bool // .*[^0-9a-zA-Z_-].*
18+
}
19+
20+
func ParseCase(s string) _Case { //nolint:cyclop,revive
21+
c := _Case{
22+
allUpper: true,
23+
allLower: true,
24+
}
25+
26+
for idx, r := range s {
27+
switch {
28+
case r >= 'A' && r <= 'Z':
29+
c.allLower = false
30+
c.hasUpper = true
31+
if idx == 0 {
32+
c.firstUpper = true
33+
}
34+
case r >= 'a' && r <= 'z':
35+
c.allUpper = false
36+
c.hasLower = true
37+
if idx == 0 {
38+
c.firstLower = true
39+
}
40+
case r == '_':
41+
c.allUpper = false
42+
c.allLower = false
43+
c.hasUnderscore = true
44+
case r == '-':
45+
c.allUpper = false
46+
c.allLower = false
47+
c.hasDash = true
48+
case r >= '0' && r <= '9':
49+
c.allUpper = false
50+
c.allLower = false
51+
c.hasNumber = true
52+
default:
53+
c.allUpper = false
54+
c.allLower = false
55+
c.hasOther = true
56+
}
57+
}
58+
return c
59+
}
60+
61+
// ex. snake_case, go, type_script, postgre_sql.
62+
func IsSnakeCase(s string) bool {
63+
return ParseCase(s).IsSnakeCase()
64+
}
65+
66+
func (c _Case) IsSnakeCase() bool {
67+
return c.hasUnderscore && c.hasLower && !c.hasUpper && !c.hasDash && !c.allLower
68+
}
69+
70+
// ex. kebab-case, go, type-script, postgre-sql.
71+
func IsKebabCase(s string) bool {
72+
return ParseCase(s).IsKebabCase()
73+
}
74+
75+
func (c _Case) IsKebabCase() bool {
76+
return c.hasDash && c.hasLower && !c.hasUpper && !c.IsSnakeCase()
77+
}
78+
79+
// ex. PascalCase, Go, TypeScript, PostgreSql.
80+
func IsPascalCase(s string) bool {
81+
return ParseCase(s).IsPascalCase()
82+
}
83+
84+
func (c _Case) IsPascalCase() bool {
85+
return c.firstUpper && !c.hasDash && !c.IsSnakeCase() && !c.IsKebabCase()
86+
}
87+
88+
// ex. camelCase, go, typeScript, postgreSql.
89+
func IsCamelCase(s string) bool {
90+
return ParseCase(s).IsCamelCase()
91+
}
92+
93+
func (c _Case) IsCamelCase() bool {
94+
return c.firstLower && !c.hasDash && !c.IsSnakeCase() && !c.IsKebabCase() && !c.IsPascalCase()
95+
}
96+
97+
func SplitSnakeCase(s string) []string {
98+
return strings.Split(s, "_")
99+
}
100+
101+
func SplitKebabCase(s string) []string {
102+
return strings.Split(s, "-")
103+
}
104+
105+
func SplitPascalCase(s string) []string {
106+
c := ParseCase(s)
107+
return c.splitPascalCase(s)
108+
}
109+
110+
func (c _Case) splitPascalCase(s string) []string {
111+
return splitCamels(s)
112+
}
113+
114+
func SplitCamelCase(s string) []string {
115+
c := ParseCase(s)
116+
return c.splitCamelCase(s)
117+
}
118+
119+
func (c _Case) splitCamelCase(s string) []string {
120+
return splitCamels(s)
121+
}
122+
123+
func SplitCase(s string) []string {
124+
c := ParseCase(s)
125+
126+
switch {
127+
case c.IsSnakeCase():
128+
return SplitSnakeCase(s)
129+
case c.IsKebabCase():
130+
return SplitKebabCase(s)
131+
case c.IsPascalCase():
132+
return c.splitPascalCase(s)
133+
case c.IsCamelCase():
134+
return c.splitCamelCase(s)
135+
default:
136+
// as default, use camelCase.
137+
return c.splitCamelCase(s)
138+
}
139+
}
140+
141+
func splitCamels(s string) []string { //nolint:cyclop
142+
var _words []string
143+
var _word []rune //nolint:prealloc
144+
145+
var before rune
146+
for _, current := range s {
147+
if !('A' <= before && before <= 'Z' || before == 0) && 'A' <= current && current <= 'Z' {
148+
// aaA -> [aa A]
149+
// ~~
150+
// | \
151+
// | +-- current
152+
// +-- before
153+
_words = append(_words, string(_word))
154+
_word = _word[:0]
155+
} else if 'A' <= before && before <= 'Z' && !('A' <= current && current <= 'Z') && len(_word) > 1 {
156+
// aAa -> [a Aa]
157+
// ~~
158+
// | \
159+
// | +-- current
160+
// +-- before
161+
_words = append(_words, string(_word[:len(_word)-1]))
162+
_word = _word[len(_word)-1:]
163+
}
164+
_word = append(_word, current)
165+
before = current
166+
}
167+
if len(_word) > 0 {
168+
_words = append(_words, string(_word))
169+
}
170+
return _words
171+
}

strings/case_test.go

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
//nolint:dupword
2+
package stringz_test
3+
4+
import (
5+
"fmt"
6+
7+
stringz "github.com/kunitsucom/util.go/strings"
8+
)
9+
10+
func ExampleIsSnakeCase() {
11+
fmt.Println(stringz.IsSnakeCase("snake_case"))
12+
fmt.Println(stringz.IsSnakeCase("kebab-case"))
13+
fmt.Println(stringz.IsSnakeCase("camelCase"))
14+
fmt.Println(stringz.IsSnakeCase("PascalCase"))
15+
fmt.Println(stringz.IsSnakeCase("go"))
16+
fmt.Println(stringz.IsSnakeCase("type_script"))
17+
fmt.Println(stringz.IsSnakeCase("postgre_sql"))
18+
fmt.Println(stringz.IsSnakeCase("goV1_21"))
19+
fmt.Println(stringz.IsSnakeCase("MySQLV8"))
20+
// Output:
21+
// true
22+
// false
23+
// false
24+
// false
25+
// false
26+
// true
27+
// true
28+
// false
29+
// false
30+
}
31+
32+
func ExampleIsKebabCase() {
33+
fmt.Println(stringz.IsKebabCase("snake_case"))
34+
fmt.Println(stringz.IsKebabCase("kebab-case"))
35+
fmt.Println(stringz.IsKebabCase("camelCase"))
36+
fmt.Println(stringz.IsKebabCase("PascalCase"))
37+
fmt.Println(stringz.IsKebabCase("go"))
38+
fmt.Println(stringz.IsKebabCase("type-script"))
39+
fmt.Println(stringz.IsKebabCase("postgre-sql"))
40+
fmt.Println(stringz.IsKebabCase("goV1_21"))
41+
fmt.Println(stringz.IsKebabCase("MySQLV8"))
42+
// Output:
43+
// false
44+
// true
45+
// false
46+
// false
47+
// false
48+
// true
49+
// true
50+
// false
51+
// false
52+
}
53+
54+
func ExampleIsCamelCase() {
55+
fmt.Println(stringz.IsCamelCase("snake_case"))
56+
fmt.Println(stringz.IsCamelCase("kebab-case"))
57+
fmt.Println(stringz.IsCamelCase("camelCase"))
58+
fmt.Println(stringz.IsCamelCase("PascalCase"))
59+
fmt.Println(stringz.IsCamelCase("go"))
60+
fmt.Println(stringz.IsCamelCase("typeScript"))
61+
fmt.Println(stringz.IsCamelCase("postgreSQL"))
62+
fmt.Println(stringz.IsCamelCase("goV1_21"))
63+
fmt.Println(stringz.IsCamelCase("MySQLV8"))
64+
// Output:
65+
// false
66+
// false
67+
// true
68+
// false
69+
// true
70+
// true
71+
// true
72+
// true
73+
// false
74+
}
75+
76+
func ExampleIsPascalCase() {
77+
fmt.Println(stringz.IsPascalCase("snake_case"))
78+
fmt.Println(stringz.IsPascalCase("kebab-case"))
79+
fmt.Println(stringz.IsPascalCase("camelCase"))
80+
fmt.Println(stringz.IsPascalCase("PascalCase"))
81+
fmt.Println(stringz.IsPascalCase("Go"))
82+
fmt.Println(stringz.IsPascalCase("TypeScript"))
83+
fmt.Println(stringz.IsPascalCase("PostgreSQL"))
84+
fmt.Println(stringz.IsPascalCase("goV1_21"))
85+
fmt.Println(stringz.IsPascalCase("MySQLV8"))
86+
// Output:
87+
// false
88+
// false
89+
// false
90+
// true
91+
// true
92+
// true
93+
// true
94+
// false
95+
// true
96+
}
97+
98+
func ExampleSplitSnakeCase() {
99+
fmt.Println(stringz.SplitSnakeCase("snake_case"))
100+
fmt.Println(stringz.SplitSnakeCase("kebab-case"))
101+
fmt.Println(stringz.SplitSnakeCase("camelCase"))
102+
fmt.Println(stringz.SplitSnakeCase("PascalCase"))
103+
fmt.Println(stringz.SplitSnakeCase("go"))
104+
fmt.Println(stringz.SplitSnakeCase("type_script"))
105+
fmt.Println(stringz.SplitSnakeCase("postgre_sql"))
106+
fmt.Println(stringz.SplitSnakeCase("goV1_21"))
107+
fmt.Println(stringz.SplitSnakeCase("MySQLV8"))
108+
// Output:
109+
// [snake case]
110+
// [kebab-case]
111+
// [camelCase]
112+
// [PascalCase]
113+
// [go]
114+
// [type script]
115+
// [postgre sql]
116+
// [goV1 21]
117+
// [MySQLV8]
118+
}
119+
120+
func ExampleSplitKebabCase() {
121+
fmt.Println(stringz.SplitKebabCase("snake_case"))
122+
fmt.Println(stringz.SplitKebabCase("kebab-case"))
123+
fmt.Println(stringz.SplitKebabCase("camelCase"))
124+
fmt.Println(stringz.SplitKebabCase("PascalCase"))
125+
fmt.Println(stringz.SplitKebabCase("go"))
126+
fmt.Println(stringz.SplitKebabCase("type-script"))
127+
fmt.Println(stringz.SplitKebabCase("postgre-sql"))
128+
fmt.Println(stringz.SplitKebabCase("goV1_21"))
129+
fmt.Println(stringz.SplitKebabCase("MySQLV8"))
130+
// Output:
131+
// [snake_case]
132+
// [kebab case]
133+
// [camelCase]
134+
// [PascalCase]
135+
// [go]
136+
// [type script]
137+
// [postgre sql]
138+
// [goV1_21]
139+
// [MySQLV8]
140+
}
141+
142+
func ExampleSplitCamelCase() {
143+
fmt.Println(stringz.SplitCamelCase("snake_case"))
144+
fmt.Println(stringz.SplitCamelCase("kebab-case"))
145+
fmt.Println(stringz.SplitCamelCase("camelCase"))
146+
fmt.Println(stringz.SplitCamelCase("PascalCase"))
147+
fmt.Println(stringz.SplitCamelCase("go"))
148+
fmt.Println(stringz.SplitCamelCase("typeScript"))
149+
fmt.Println(stringz.SplitCamelCase("postgreSQL"))
150+
fmt.Println(stringz.SplitCamelCase("goV1_21"))
151+
fmt.Println(stringz.SplitCamelCase("MySQLV8"))
152+
// Output:
153+
// [snake_case]
154+
// [kebab-case]
155+
// [camel Case]
156+
// [Pascal Case]
157+
// [go]
158+
// [type Script]
159+
// [postgre SQL]
160+
// [go V1_21]
161+
// [My SQL V8]
162+
}
163+
164+
func ExampleSplitPascalCase() {
165+
fmt.Println(stringz.SplitPascalCase("snake_case"))
166+
fmt.Println(stringz.SplitPascalCase("kebab-case"))
167+
fmt.Println(stringz.SplitPascalCase("camelCase"))
168+
fmt.Println(stringz.SplitPascalCase("PascalCase"))
169+
fmt.Println(stringz.SplitPascalCase("Go"))
170+
fmt.Println(stringz.SplitPascalCase("TypeScript"))
171+
fmt.Println(stringz.SplitPascalCase("PostgreSQL"))
172+
fmt.Println(stringz.SplitPascalCase("goV1_21"))
173+
fmt.Println(stringz.SplitPascalCase("MySQLV8"))
174+
// Output:
175+
// [snake_case]
176+
// [kebab-case]
177+
// [camel Case]
178+
// [Pascal Case]
179+
// [Go]
180+
// [Type Script]
181+
// [Postgre SQL]
182+
// [go V1_21]
183+
// [My SQL V8]
184+
}
185+
186+
func ExampleSplitCase() {
187+
fmt.Println(stringz.SplitCase("snake_case"))
188+
fmt.Println(stringz.SplitCase("kebab-case"))
189+
fmt.Println(stringz.SplitCase("camelCase"))
190+
fmt.Println(stringz.SplitCase("PascalCase"))
191+
fmt.Println(stringz.SplitCase("go"))
192+
fmt.Println(stringz.SplitCase("typeScript"))
193+
fmt.Println(stringz.SplitCase("postgreSQL"))
194+
fmt.Println(stringz.SplitCase("goV1_21"))
195+
fmt.Println(stringz.SplitCase("MySQLV8"))
196+
fmt.Println(stringz.SplitCase("A - 3"))
197+
// Output:
198+
// [snake case]
199+
// [kebab case]
200+
// [camel Case]
201+
// [Pascal Case]
202+
// [go]
203+
// [type Script]
204+
// [postgre SQL]
205+
// [go V1_21]
206+
// [My SQL V8]
207+
// [A - 3]
208+
}

0 commit comments

Comments
 (0)