Skip to content

Commit

Permalink
Adding functions
Browse files Browse the repository at this point in the history
  • Loading branch information
spekary committed Nov 12, 2024
1 parent 20d1621 commit 772184f
Show file tree
Hide file tree
Showing 13 changed files with 1,274 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,9 @@ go.work.sum

# env file
.env

# OSs
.DS_Store

# IDEs
/.idea
81 changes: 81 additions & 0 deletions case.go
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)
}
109 changes: 109 additions & 0 deletions case_test.go
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)
}
})
}
}
5 changes: 5 additions & 0 deletions go.mod
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
2 changes: 2 additions & 0 deletions go.sum
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=
63 changes: 63 additions & 0 deletions numbers.go
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)
}
}
Loading

0 comments on commit 772184f

Please sign in to comment.