Skip to content

Commit

Permalink
Add Header-Case
Browse files Browse the repository at this point in the history
  • Loading branch information
abemedia committed Jan 10, 2023
1 parent 26d5b1a commit c2adeb5
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 25 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
.PHONY: benchmark docs lint test

docs:
which godoc2ghmd || ( go get github.com/DevotedHealth/godoc2ghmd && go mod tidy )
which godoc2ghmd || go install github.com/DevotedHealth/godoc2ghmd
godoc2ghmd -template .readme.tmpl github.com/ettle/strcase > README.md

test:
go test -cover ./...

lint:
which golangci-lint || ( go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.27.0 && go mod tidy )
which golangci-lint || go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.27.0
golangci-lint run
golangci-lint run benchmark/*.go

Expand Down
70 changes: 47 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Example usage

strcase.ToKebab("helloWorld") // hello-world
strcase.ToKEBAB("helloWorld") // HELLO-WORLD
strcase.ToHeader("helloWorld") // Hello-World

strcase.ToPascal("hello-world") // HelloWorld
strcase.ToCamel("hello-world") // helloWorld
Expand All @@ -32,7 +33,7 @@ Example usage
strcase.ToSnake("FOOBar") // foo_bar

// Support Go initialisms
strcase.ToGoCamel("http_response") // HTTPResponse
strcase.ToGoPascal("http_response") // HTTPResponse

// Specify case and delimiter
strcase.ToCase("HelloWorld", strcase.UpperCase, '.') // HELLO.WORLD
Expand Down Expand Up @@ -76,7 +77,7 @@ the initialisms if you wish to add additional ones, such as "SSL" or "CMS" or
domain specific ones to your industry.


ToGoCamel("http_response") // HTTPResponse
ToGoPascal("http_response") // HTTPResponse
ToGoSnake("http_response") // HTTP_response

### Test coverage
Expand All @@ -96,35 +97,35 @@ Hopefully I was fair to each library and happy to rerun benchmarks differently
or reword my commentary based on suggestions or updates.


// This package
// Go intialisms and custom casers are slower
BenchmarkToTitle-4 992491 1559 ns/op 32 B/op 1 allocs/op
BenchmarkToSnake-4 1000000 1475 ns/op 32 B/op 1 allocs/op
BenchmarkToSNAKE-4 1000000 1609 ns/op 32 B/op 1 allocs/op
BenchmarkToGoSnake-4 275010 3697 ns/op 44 B/op 4 allocs/op
BenchmarkToCustomCaser-4 342704 4191 ns/op 56 B/op 4 allocs/op
// This package - faster then almost all libraries
// Initialisms are more complicated and slightly slower, but still faster then other libraries that do less
BenchmarkToTitle-4 7821166 221 ns/op 32 B/op 1 allocs/op
BenchmarkToSnake-4 9378589 202 ns/op 32 B/op 1 allocs/op
BenchmarkToSNAKE-4 6174453 223 ns/op 32 B/op 1 allocs/op
BenchmarkToGoSnake-4 3114266 434 ns/op 44 B/op 4 allocs/op
BenchmarkToCustomCaser-4 2973855 448 ns/op 56 B/op 4 allocs/op

// Segment has very fast snake case and camel case libraries
// No features or customization, but very very fast
BenchmarkSegment-4 1303809 938 ns/op 16 B/op 1 allocs/op
BenchmarkSegment-4 24003495 64.9 ns/op 16 B/op 1 allocs/op

// Stdlib strings.Title for comparison, even though it only splits on spaces
BenchmarkToTitleStrings-4 1213467 1164 ns/op 16 B/op 1 allocs/op
BenchmarkToTitleStrings-4 11259376 161 ns/op 16 B/op 1 allocs/op

// Other libraries or code snippets
// - Most are slower, by up to an order of magnitude
// - None support initialisms or customization
// - Some generate only camelCase or snake_case
// - Many lack unicode support
BenchmarkToSnakeStoewer-4 973200 2075 ns/op 64 B/op 2 allocs/op
BenchmarkToSnakeStoewer-4 7103268 297 ns/op 64 B/op 2 allocs/op
// Copying small rune arrays is slow
BenchmarkToSnakeSiongui-4 264315 4229 ns/op 48 B/op 10 allocs/op
BenchmarkGoValidator-4 206811 5152 ns/op 184 B/op 9 allocs/op
BenchmarkToSnakeSiongui-4 3710768 413 ns/op 48 B/op 10 allocs/op
BenchmarkGoValidator-4 2416479 1049 ns/op 184 B/op 9 allocs/op
// String alloction is slow
BenchmarkToSnakeFatih-4 82675 12280 ns/op 392 B/op 26 allocs/op
BenchmarkToSnakeIanColeman-4 83276 13903 ns/op 145 B/op 13 allocs/op
BenchmarkToSnakeFatih-4 1000000 2407 ns/op 624 B/op 26 allocs/op
BenchmarkToSnakeIanColeman-4 1005766 1426 ns/op 160 B/op 13 allocs/op
// Regexp is slow
BenchmarkToSnakeGolangPrograms-4 74448 18586 ns/op 176 B/op 11 allocs/op
BenchmarkToSnakeGolangPrograms-4 614689 2237 ns/op 225 B/op 11 allocs/op

// These results aren't a surprise - my initial version of this library was
// painfully slow. I think most of us, without spending some time with
Expand Down Expand Up @@ -168,6 +169,7 @@ custom casers to mimic the behavior of the other package.
* [func ToGoKebab(s string) string](#ToGoKebab)
* [func ToGoPascal(s string) string](#ToGoPascal)
* [func ToGoSnake(s string) string](#ToGoSnake)
* [func ToHeader(s string) string](#ToHeader)
* [func ToKEBAB(s string) string](#ToKEBAB)
* [func ToKebab(s string) string](#ToKebab)
* [func ToPascal(s string) string](#ToPascal)
Expand All @@ -177,6 +179,7 @@ custom casers to mimic the behavior of the other package.
* [func NewCaser(goInitialisms bool, initialismOverrides map[string]bool, splitFn SplitFn) *Caser](#NewCaser)
* [func (c *Caser) ToCamel(s string) string](#Caser.ToCamel)
* [func (c *Caser) ToCase(s string, wordCase WordCase, delimiter rune) string](#Caser.ToCase)
* [func (c *Caser) ToHeader(s string) string](#Caser.ToHeader)
* [func (c *Caser) ToKEBAB(s string) string](#Caser.ToKEBAB)
* [func (c *Caser) ToKebab(s string) string](#Caser.ToKebab)
* [func (c *Caser) ToPascal(s string) string](#Caser.ToPascal)
Expand All @@ -201,26 +204,28 @@ Also known as lowerCamelCase or mixedCase.



## <a name="ToCase">func</a> [ToCase](./strcase.go#L70)
## <a name="ToCase">func</a> [ToCase](./strcase.go#L77)
``` go
func ToCase(s string, wordCase WordCase, delimiter rune) string
```
ToCase returns words in given case and delimiter.



## <a name="ToGoCamel">func</a> [ToGoCamel](./strcase.go#L65)
## <a name="ToGoCamel">func</a> [ToGoCamel](./strcase.go#L67)
``` go
func ToGoCamel(s string) string
```
ToGoCamel returns words in camelCase (capitalized words concatenated together, with first word lower case).
Also known as lowerCamelCase or mixedCase.

Respects Go's common initialisms (e.g. httpResponse -> HTTPResponse).
Respects Go's common initialisms, but first word remains lowercased which is
important for code generator use cases (e.g. toJson -> toJSON, httpResponse
-> httpResponse).



## <a name="ToGoCase">func</a> [ToGoCase](./strcase.go#L77)
## <a name="ToGoCase">func</a> [ToGoCase](./strcase.go#L84)
``` go
func ToGoCase(s string, wordCase WordCase, delimiter rune) string
```
Expand Down Expand Up @@ -262,6 +267,14 @@ Respects Go's common initialisms (e.g. http_response -> HTTP_response).



## <a name="ToHeader">func</a> [ToHeader](./strcase.go#L72)
``` go
func ToHeader(s string) string
```
ToHeader returns words in Header-Case (capitalized words with dashes).



## <a name="ToKEBAB">func</a> [ToKEBAB](./strcase.go#L37)
``` go
func ToKEBAB(s string) string
Expand Down Expand Up @@ -357,7 +370,7 @@ Also known as lowerCamelCase or mixedCase.



### <a name="Caser.ToCase">func</a> (\*Caser) [ToCase](./caser.go#L85)
### <a name="Caser.ToCase">func</a> (\*Caser) [ToCase](./caser.go#L90)
``` go
func (c *Caser) ToCase(s string, wordCase WordCase, delimiter rune) string
```
Expand All @@ -366,6 +379,15 @@ ToCase returns words with a given case and delimiter.



### <a name="Caser.ToHeader">func</a> (\*Caser) [ToHeader](./caser.go#L85)
``` go
func (c *Caser) ToHeader(s string) string
```
ToHeader returns words in Header-Case (capitalized words with dashes).




### <a name="Caser.ToKEBAB">func</a> (\*Caser) [ToKEBAB](./caser.go#L68)
``` go
func (c *Caser) ToKEBAB(s string) string
Expand Down Expand Up @@ -469,7 +491,6 @@ NewSplitFn returns a SplitFn based on the options provided.
NewSplitFn covers the majority of common options that other strcase
libraries provide and should allow you to simply create a custom caser.
For more complicated use cases, feel free to write your own SplitFn
nolint:gocyclo



Expand Down Expand Up @@ -524,6 +545,9 @@ const (
// TitleCase - Only first letter upper cased (Example)
TitleCase
// CamelCase - TitleCase except lower case first word (exampleText)
// Notably, even if the first word is an initialism, it will be lower
// cased. This is important for code generators where capital letters
// mean exported functions. i.e. jsonString(), not JSONString()
CamelCase
)
```
Expand Down
10 changes: 10 additions & 0 deletions benchmark/benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ func BenchmarkToSNAKE(b *testing.B) {
b.Fatalf("Expected %s, got %s", expected, s)
}
}
func BenchmarkToHeader(b *testing.B) {
var s string
for n := 0; n < b.N; n++ {
s = strcase.ToHeader(testCamel)
}
expected := testHeader
if expected != s {
b.Fatalf("Expected %s, got %s", expected, s)
}
}
func BenchmarkToGoSnake(b *testing.B) {
var s string
for n := 0; n < b.N; n++ {
Expand Down
5 changes: 5 additions & 0 deletions caser.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ func (c *Caser) ToCamel(s string) string {
return convert(s, c.splitFn, '\x00', CamelCase, c.initialisms)
}

// ToHeader returns words in Header-Case (capitalized words with dashes).
func (c *Caser) ToHeader(s string) string {
return convert(s, c.splitFn, '-', TitleCase, c.initialisms)
}

// ToCase returns words with a given case and delimiter.
func (c *Caser) ToCase(s string, wordCase WordCase, delimiter rune) string {
return convert(s, c.splitFn, delimiter, wordCase, c.initialisms)
Expand Down
3 changes: 3 additions & 0 deletions caser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func TestCaserAll(t *testing.T) {
KEBAB string
pascal string
camel string
header string
title string
}
for _, test := range []data{
Expand All @@ -29,6 +30,7 @@ func TestCaserAll(t *testing.T) {
KEBAB: "HELLO-WORLD!",
pascal: "HelloWorld!",
camel: "helloWorld!",
header: "Hello-World!",
title: "Hello World!",
},
} {
Expand All @@ -41,6 +43,7 @@ func TestCaserAll(t *testing.T) {
KEBAB: c.ToKEBAB(test.input),
pascal: c.ToPascal(test.input),
camel: c.ToCamel(test.input),
header: c.ToHeader(test.input),
title: c.ToCase(test.input, TitleCase, ' '),
}
assert.Equal(t, test, output)
Expand Down
1 change: 1 addition & 0 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Example usage
strcase.ToKebab("helloWorld") // hello-world
strcase.ToKEBAB("helloWorld") // HELLO-WORLD
strcase.ToHeader("helloWorld") // Hello-World
strcase.ToPascal("hello-world") // HelloWorld
strcase.ToCamel("hello-world") // helloWorld
Expand Down
5 changes: 5 additions & 0 deletions strcase.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ func ToGoCamel(s string) string {
return convertWithGoInitialisms(s, 0, CamelCase)
}

// ToHeader returns words in Header-Case (capitalized words with dashes).
func ToHeader(s string) string {
return convertWithoutInitialisms(s, '-', TitleCase)
}

// ToCase returns words in given case and delimiter.
func ToCase(s string, wordCase WordCase, delimiter rune) string {
return convertWithoutInitialisms(s, delimiter, wordCase)
Expand Down
Loading

0 comments on commit c2adeb5

Please sign in to comment.