-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
1,274 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,3 +23,9 @@ go.work.sum | |
|
||
# env file | ||
.env | ||
|
||
# OSs | ||
.DS_Store | ||
|
||
# IDEs | ||
/.idea |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package strings | ||
|
||
import ( | ||
"strings" | ||
"unicode" | ||
) | ||
|
||
// KebabToCamel convert string s from kebab-case to CamelCase. The initial case of s does not affect the output. | ||
func KebabToCamel(s string) string { | ||
// This depends on Map walking the string from start to end, which | ||
// really needs to happen anyway in unicode strings. | ||
capitalize := true | ||
return strings.Map(func(r rune) rune { | ||
if r == '-' { | ||
capitalize = true | ||
return -1 | ||
} else if capitalize { | ||
capitalize = false | ||
return unicode.ToTitle(r) | ||
} else { | ||
return unicode.ToLower(r) | ||
} | ||
}, s) | ||
} | ||
|
||
// SnakeToKebab converts s from snake_case to kebab-case. Only the underscores are impacted, the case of the rest of s | ||
// is passed through unchanged. | ||
func SnakeToKebab(s string) string { | ||
return strings.Replace(s, "_", "-", -1) | ||
} | ||
|
||
// CamelToKebab converts capitalize from CamelCase to kebab-case. | ||
// If it encounters a character that is not legitimate camel case, | ||
// it ignores it (like numbers, spaces, etc.). | ||
// Runs of upper case letters are treated as one word. | ||
func CamelToKebab(camelCase string) string { | ||
return camelToKebabOrSnake(camelCase, '-') | ||
} | ||
|
||
// CamelToSnake converts camelCase from CamelCase to snake_case. | ||
// If it encounters a character that is not legitimate camel case, | ||
// it ignores it (like numbers, spaces, etc.). | ||
// Runs of upper case letters are treated as one word. | ||
// A run of upper case, followed by lower case letters will be treated | ||
// as if the final character in the upper case run belongs with the lower case | ||
// letters. | ||
func CamelToSnake(camelCase string) string { | ||
return camelToKebabOrSnake(camelCase, '_') | ||
} | ||
|
||
func camelToKebabOrSnake(camelCase string, replacement rune) string { | ||
var kebabCase []rune | ||
var inUpper bool | ||
|
||
for i, r := range camelCase { | ||
if unicode.IsLetter(r) { | ||
if unicode.IsUpper(r) { | ||
if i > 0 && !inUpper { | ||
kebabCase = append(kebabCase, replacement) | ||
} | ||
kebabCase = append(kebabCase, unicode.ToLower(r)) | ||
inUpper = true | ||
|
||
} else { | ||
if inUpper { | ||
// switching from upper to lower, if we were in an upper run | ||
// we need to add a hyphen in front of the last rune | ||
if len(kebabCase) > 1 && kebabCase[len(kebabCase)-2] != replacement { | ||
r2 := kebabCase[len(kebabCase)-1] | ||
kebabCase[len(kebabCase)-1] = replacement | ||
kebabCase = append(kebabCase, r2) | ||
} | ||
} | ||
kebabCase = append(kebabCase, r) | ||
inUpper = false | ||
} | ||
} | ||
} | ||
|
||
return string(kebabCase) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package strings | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
) | ||
|
||
func ExampleKebabToCamel() { | ||
a := KebabToCamel("abc-def") | ||
fmt.Println(a) | ||
//Output: AbcDef | ||
} | ||
|
||
func TestKebabToCamel(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
s string | ||
want string | ||
}{ | ||
{"empty", "", ""}, | ||
{"a", "a", "A"}, | ||
{"-a", "-a", "A"}, | ||
{"b-a", "b-a", "BA"}, | ||
{"123-abc", "123-abc", "123Abc"}, | ||
{"this-that", "this-that", "ThisThat"}, | ||
{"This-THAT", "This-THAT", "ThisThat"}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
if got := KebabToCamel(tt.s); got != tt.want { | ||
t.Errorf("KebabToCamel() = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func ExampleSnakeToKebab() { | ||
a := SnakeToKebab("abc_def") | ||
fmt.Println(a) | ||
//Output: abc-def | ||
} | ||
|
||
func TestSnakeToKebab(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
s string | ||
want string | ||
}{ | ||
{"empty", "", ""}, | ||
{"a_b", "a_b", "a-b"}, | ||
{"-b", "-b", "-b"}, | ||
{"_b", "_b", "-b"}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
if got := SnakeToKebab(tt.s); got != tt.want { | ||
t.Errorf("SnakeToKebab() = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func ExampleCamelToKebab() { | ||
a := CamelToKebab("AbcDef") | ||
fmt.Println(a) | ||
b := CamelToKebab("AbcDEFghi") | ||
fmt.Println(b) | ||
//Output: abc-def | ||
//abc-de-fghi | ||
} | ||
|
||
func ExampleCamelToSnake() { | ||
a := CamelToSnake("AbcDef") | ||
fmt.Println(a) | ||
b := CamelToSnake("AbcDEFghi") | ||
fmt.Println(b) | ||
//Output: abc_def | ||
//abc_de_fghi | ||
} | ||
|
||
func TestCamelToKebab(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
s string | ||
want string | ||
}{ | ||
{"empty", "", ""}, | ||
{"a", "a", "a"}, | ||
{"A", "A", "a"}, | ||
{"ab", "ab", "ab"}, | ||
{"AB", "AB", "ab"}, | ||
{"Ab", "Ab", "ab"}, | ||
{"aB", "aB", "a-b"}, | ||
{"Abc", "Abc", "abc"}, | ||
{"AbC", "AbC", "ab-c"}, | ||
{"ABc", "ABc", "a-bc"}, | ||
{"a1b", "a1b", "ab"}, | ||
{"ABC", "ABC", "abc"}, | ||
{"ABCd", "ABCd", "ab-cd"}, | ||
{"AbCdE", "ABCdE", "ab-cd-e"}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
if got := CamelToKebab(tt.s); got != tt.want { | ||
t.Errorf("SnakeToKebab() = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module github.com/goradd/strings | ||
|
||
go 1.23.2 | ||
|
||
require golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= | ||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package strings | ||
|
||
import ( | ||
"golang.org/x/exp/constraints" | ||
"strconv" | ||
"strings" | ||
"unicode" | ||
) | ||
|
||
// ExtractNumbers returns a string with the digits contained in the given string. | ||
func ExtractNumbers(in string) string { | ||
return strings.Map(func(r rune) rune { | ||
if unicode.IsNumber(r) { | ||
return r | ||
} | ||
return -1 | ||
}, in) | ||
} | ||
|
||
// AtoI is a convenience script for converting a string to various types of signed integers. | ||
// An invalid input will return zero, including if the input overflows the max size of the integer type. | ||
func AtoI[T constraints.Integer](s string) T { | ||
var t T | ||
signed := true | ||
bitSize := 0 | ||
switch any(t).(type) { | ||
case uint: | ||
signed = false | ||
case uint8: | ||
signed = false | ||
bitSize = 8 | ||
case uint16: | ||
signed = false | ||
bitSize = 16 | ||
case uint32: | ||
signed = false | ||
bitSize = 32 | ||
case uint64: | ||
signed = false | ||
bitSize = 64 | ||
case int8: | ||
bitSize = 8 | ||
case int16: | ||
bitSize = 16 | ||
case int32: | ||
bitSize = 32 | ||
case int64: | ||
bitSize = 64 | ||
} | ||
if signed { | ||
v, err := strconv.ParseInt(s, 10, bitSize) | ||
if err != nil { | ||
return 0 | ||
} | ||
return T(v) | ||
} else { | ||
v, err := strconv.ParseUint(s, 10, 0) | ||
if err != nil { | ||
return 0 | ||
} | ||
return T(v) | ||
} | ||
} |
Oops, something went wrong.