Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .cursor/commands/pr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Create a Pull Request

- Make sure local changes are committed and pushed, if not ask user to do it
- Compare the current branch to origin/main
- Read through the changes and commits
- Raise a pull request using the gh cli, you might have to `unset GITHUB_TOKEN` if auth issues occur.
- Don't ever fork, we work directly on the originally repo with branches
- Use a succint title
- Keep the description to the point and do not hallucinate
- Use a temporary file for PR description and remove it later with rm linux command.
134 changes: 130 additions & 4 deletions sx.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,13 +169,25 @@ func capitalizeWord(word string) string {
}

// joinWords joins words with a separator
func joinWords(words []string, separator string, transform func(string, int) string) string {
func joinWords(words []string, separator string, preserveEmpty bool, transform func(string, int) string) string {
if len(words) == 0 {
return ""
}

// Filter out empty words if not preserving them
wordsToUse := words
if !preserveEmpty {
var filteredWords []string
for _, word := range words {
if word != "" {
filteredWords = append(filteredWords, word)
}
}
wordsToUse = filteredWords
}

var result strings.Builder
for i, word := range words {
for i, word := range wordsToUse {
if i > 0 && separator != "" {
result.WriteString(separator)
}
Expand Down Expand Up @@ -215,20 +227,134 @@ func PascalCase[T StringOrStringSlice](input T, opts ...CaseOption) string {
switch v := any(input).(type) {
case string:
words := splitByCaseWithCustomSeparators(v, nil)
result := joinWords(words, "", func(word string, i int) string {
result := joinWords(words, "", false, func(word string, i int) string {
normalized := normalizeWord(word, options.Normalize)
return capitalizeWord(normalized)
})

return result
case []string:
result := joinWords(v, "", false, func(word string, i int) string {
normalized := normalizeWord(word, options.Normalize)
return capitalizeWord(normalized)
})

return result
default:
return ""
}
}

// lowercaseWord converts the first letter to lowercase
func lowercaseWord(word string) string {
if word == "" {
return word
}

r, size := utf8.DecodeRuneInString(word)
if size == 0 {
return word
}

return string(unicode.ToLower(r)) + word[size:]
}

// CamelCase converts input to camelCase
func CamelCase[T StringOrStringSlice](input T, opts ...CaseOption) string {
switch v := any(input).(type) {
case string:
pascalCase := PascalCase(v, opts...)
return lowercaseWord(pascalCase)
case []string:
result := joinWords(v, "", func(word string, i int) string {
if len(v) == 0 {
return ""
}

options := CaseConfig{}
for _, opt := range opts {
opt(&options)
}

result := joinWords(v, "", false, func(word string, i int) string {
normalized := normalizeWord(word, options.Normalize)
if i == 0 {
return lowercaseWord(normalized)
}

return capitalizeWord(normalized)
})
return result
default:
return ""
}
}

// KebabCase converts input to kebab-case
func KebabCase[T StringOrStringSlice](input T, separator ...string) string {
sep := "-"
if len(separator) > 0 {
sep = separator[0]
}

switch v := any(input).(type) {
case string:
words := splitByCaseWithCustomSeparators(v, nil)
result := joinWords(words, sep, true, func(word string, i int) string {
return strings.ToLower(word)
})
return result
case []string:
result := joinWords(v, sep, true, func(word string, i int) string {
return strings.ToLower(word)
})
return result
default:
return ""
}
}

// SnakeCase converts input to snake_case
func SnakeCase[T StringOrStringSlice](input T) string {
return KebabCase(input, "_")
}

// TrainCase converts input to Train-Case
func TrainCase[T StringOrStringSlice](input T, opts ...CaseOption) string {
options := CaseConfig{}
for _, opt := range opts {
opt(&options)
}

switch v := any(input).(type) {
case string:
words := splitByCaseWithCustomSeparators(v, nil)
result := joinWords(words, "-", false, func(word string, i int) string {
normalized := normalizeWord(word, options.Normalize)
return capitalizeWord(normalized)
})
return result
case []string:
result := joinWords(v, "-", false, func(word string, i int) string {
normalized := normalizeWord(word, options.Normalize)
return capitalizeWord(normalized)
})
return result
default:
return ""
}
}

// FlatCase converts input to flatcase (no separators)
func FlatCase[T StringOrStringSlice](input T) string {
return KebabCase(input, "")
}

// UpperFirst converts the first character to uppercase
func UpperFirst(s string) string {
return capitalizeWord(s)
}

// LowerFirst converts the first character to lowercase
func LowerFirst(s string) string {
return lowercaseWord(s)
}
Loading