Skip to content

Commit de65840

Browse files
committed
Add ToPascalGoCase and ToCamelGoCase
1 parent a027088 commit de65840

File tree

3 files changed

+69
-32
lines changed

3 files changed

+69
-32
lines changed

strcase/doc.go

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Package strcase regroups functions to convert between PascalCase, camelCase, and snake_case.
2+
// ToPascalGoCase and ToCamelGoCase are also provided, which recognizes some common initialisms and always transforms them to uppercase.
3+
package strcase

strcase/id.go

+21-8
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,29 @@ import (
66
"unicode"
77
)
88

9+
// ToPascalCase transforms a string in any form to PascalCase.
910
func ToPascalCase(input string) string {
10-
return splitJoin(input, 0, 0)
11+
return splitJoin(input, 0, 0, false)
1112
}
1213

14+
// ToPascalGoCase transforms a string in any form to PascalCase, but with recognized initialisms in uppercase, matching the Go style.
15+
func ToPascalGoCase(input string) string {
16+
return splitJoin(input, 0, 0, true)
17+
}
18+
19+
// ToCamelCase transforms a string in any form to camelCase.
1320
func ToCamelCase(input string) string {
14-
return splitJoin(input, 1, 0)
21+
return splitJoin(input, 1, 0, false)
22+
}
23+
24+
// ToCamelGoCase transforms a string in any form to camelCase, but with recognized initialisms in uppercase, matching the Go style.
25+
func ToCamelGoCase(input string) string {
26+
return splitJoin(input, 1, 0, true)
1527
}
1628

29+
// ToSnakeCase transforms a string in any form to snake_case.
1730
func ToSnakeCase(input string) string {
18-
return splitJoin(input, math.MaxInt64, '_')
31+
return splitJoin(input, math.MaxInt64, '_', false)
1932
}
2033

2134
func allocateBuilder(input string, separator rune) *strings.Builder {
@@ -35,7 +48,7 @@ func allocateBuilder(input string, separator rune) *strings.Builder {
3548
return &b
3649
}
3750

38-
func splitJoin(input string, firstUpper int, separator rune) string {
51+
func splitJoin(input string, firstUpper int, separator rune, initialism bool) string {
3952
b := allocateBuilder(input, separator)
4053
var buf []rune
4154
var currentPartIndex int
@@ -51,7 +64,7 @@ func splitJoin(input string, firstUpper int, separator rune) string {
5164
b.WriteRune(separator)
5265
}
5366
if currentPartIndex >= firstUpper {
54-
pascalPart(buf)
67+
pascalPart(buf, initialism)
5568
}
5669
for _, r := range buf {
5770
b.WriteRune(r)
@@ -85,10 +98,10 @@ func splitJoin(input string, firstUpper int, separator rune) string {
8598
return b.String()
8699
}
87100

88-
// Convert to uppercase if initialism.
101+
// Convert to uppercase if initialism and `initialism` is true.
89102
// Convert first rune to uppercase otherwise.
90-
func pascalPart(part []rune) {
91-
if isInitialism(part) {
103+
func pascalPart(part []rune, initialism bool) {
104+
if initialism && isInitialism(part) {
92105
for ri, r := range part {
93106
part[ri] = unicode.ToUpper(r)
94107
}

strcase/id_test.go

+45-24
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,12 @@ func Benchmark_category(b *testing.B) {
100100

101101
func Test_splitJoin(t *testing.T) {
102102
tests := []struct {
103-
input string
104-
camel string
105-
pascal string
106-
snake string
103+
input string
104+
camel string
105+
camelGo string
106+
pascal string
107+
pascalGo string
108+
snake string
107109
}{
108110
{
109111
// everything empty
@@ -139,10 +141,12 @@ func Test_splitJoin(t *testing.T) {
139141
snake: "aa_bbb",
140142
},
141143
{
142-
input: "aa_id",
143-
pascal: "AaID",
144-
camel: "aaID",
145-
snake: "aa_id",
144+
input: "aa_id",
145+
pascal: "AaId",
146+
pascalGo: "AaID",
147+
camel: "aaId",
148+
camelGo: "aaID",
149+
snake: "aa_id",
146150
},
147151
{
148152
input: "fooBar",
@@ -157,35 +161,52 @@ func Test_splitJoin(t *testing.T) {
157161
snake: "foo_bar",
158162
},
159163
{
160-
input: "fooUrl",
161-
pascal: "FooURL",
162-
camel: "fooURL",
163-
snake: "foo_url",
164+
input: "fooUrl",
165+
pascal: "FooUrl",
166+
pascalGo: "FooURL",
167+
camel: "fooUrl",
168+
camelGo: "fooURL",
169+
snake: "foo_url",
164170
},
165171
{
166-
input: "fooURL",
167-
pascal: "FooURL",
168-
camel: "fooURL",
169-
snake: "foo_url",
172+
input: "fooURL",
173+
pascal: "FooUrl",
174+
pascalGo: "FooURL",
175+
camel: "fooUrl",
176+
camelGo: "fooURL",
177+
snake: "foo_url",
170178
},
171179
{
172-
input: "url10",
173-
pascal: "URL10",
174-
camel: "url10",
175-
snake: "url_10",
180+
input: "url10",
181+
pascal: "Url10",
182+
pascalGo: "URL10",
183+
camel: "url10",
184+
snake: "url_10",
176185
},
177186
{
178-
input: "url_id",
179-
pascal: "URLID",
180-
camel: "urlID",
181-
snake: "url_id",
187+
input: "url_id",
188+
pascal: "UrlId",
189+
pascalGo: "URLID",
190+
camel: "urlId",
191+
camelGo: "urlID",
192+
snake: "url_id",
182193
},
183194
}
184195
for _, tt := range tests {
185196
t.Run(tt.input, func(t *testing.T) {
186197
require.Equal(t, tt.pascal, ToPascalCase(tt.input))
187198
require.Equal(t, tt.camel, ToCamelCase(tt.input))
188199
require.Equal(t, tt.snake, ToSnakeCase(tt.input))
200+
201+
if tt.pascalGo == "" {
202+
tt.pascalGo = tt.pascal
203+
}
204+
require.Equal(t, tt.pascalGo, ToPascalGoCase(tt.input))
205+
206+
if tt.camelGo == "" {
207+
tt.camelGo = tt.camel
208+
}
209+
require.Equal(t, tt.camelGo, ToCamelGoCase(tt.input))
189210
})
190211
}
191212
}

0 commit comments

Comments
 (0)