285 lines
5.9 KiB
Go
285 lines
5.9 KiB
Go
package bun
|
|
|
|
import (
|
|
"strings"
|
|
"unicode"
|
|
)
|
|
|
|
// SnakeCaseToPascalCase converts snake_case to PascalCase
|
|
// Examples: user_id → UserID, http_request → HTTPRequest
|
|
func SnakeCaseToPascalCase(s string) string {
|
|
if s == "" {
|
|
return ""
|
|
}
|
|
|
|
parts := strings.Split(s, "_")
|
|
for i, part := range parts {
|
|
parts[i] = capitalize(part)
|
|
}
|
|
return strings.Join(parts, "")
|
|
}
|
|
|
|
// SnakeCaseToCamelCase converts snake_case to camelCase
|
|
// Examples: user_id → userID, http_request → httpRequest
|
|
func SnakeCaseToCamelCase(s string) string {
|
|
if s == "" {
|
|
return ""
|
|
}
|
|
|
|
parts := strings.Split(s, "_")
|
|
for i, part := range parts {
|
|
if i == 0 {
|
|
parts[i] = strings.ToLower(part)
|
|
} else {
|
|
parts[i] = capitalize(part)
|
|
}
|
|
}
|
|
return strings.Join(parts, "")
|
|
}
|
|
|
|
// PascalCaseToSnakeCase converts PascalCase to snake_case
|
|
// Examples: UserID → user_id, HTTPRequest → http_request
|
|
func PascalCaseToSnakeCase(s string) string {
|
|
if s == "" {
|
|
return ""
|
|
}
|
|
|
|
var result strings.Builder
|
|
var prevUpper bool
|
|
var nextUpper bool
|
|
|
|
runes := []rune(s)
|
|
for i, r := range runes {
|
|
isUpper := unicode.IsUpper(r)
|
|
|
|
if i+1 < len(runes) {
|
|
nextUpper = unicode.IsUpper(runes[i+1])
|
|
} else {
|
|
nextUpper = false
|
|
}
|
|
|
|
if i > 0 && isUpper {
|
|
// Add underscore before uppercase letter if:
|
|
// 1. Previous char was lowercase, OR
|
|
// 2. Next char is lowercase (end of acronym)
|
|
if !prevUpper || (!nextUpper && i+1 < len(runes)) {
|
|
result.WriteRune('_')
|
|
}
|
|
}
|
|
|
|
result.WriteRune(unicode.ToLower(r))
|
|
prevUpper = isUpper
|
|
}
|
|
|
|
return result.String()
|
|
}
|
|
|
|
// capitalize capitalizes the first letter and handles common acronyms
|
|
func capitalize(s string) string {
|
|
if s == "" {
|
|
return ""
|
|
}
|
|
|
|
upper := strings.ToUpper(s)
|
|
|
|
// Handle common acronyms
|
|
acronyms := map[string]bool{
|
|
"ID": true,
|
|
"UUID": true,
|
|
"GUID": true,
|
|
"URL": true,
|
|
"URI": true,
|
|
"HTTP": true,
|
|
"HTTPS": true,
|
|
"API": true,
|
|
"JSON": true,
|
|
"XML": true,
|
|
"SQL": true,
|
|
"HTML": true,
|
|
"CSS": true,
|
|
"RID": true,
|
|
}
|
|
|
|
if acronyms[upper] {
|
|
return upper
|
|
}
|
|
|
|
// Capitalize first letter
|
|
runes := []rune(s)
|
|
runes[0] = unicode.ToUpper(runes[0])
|
|
return string(runes)
|
|
}
|
|
|
|
// Pluralize converts a singular word to plural
|
|
// Basic implementation with common rules
|
|
func Pluralize(s string) string {
|
|
if s == "" {
|
|
return ""
|
|
}
|
|
|
|
// Special cases
|
|
irregular := map[string]string{
|
|
"person": "people",
|
|
"child": "children",
|
|
"tooth": "teeth",
|
|
"foot": "feet",
|
|
"man": "men",
|
|
"woman": "women",
|
|
"mouse": "mice",
|
|
"goose": "geese",
|
|
"ox": "oxen",
|
|
"datum": "data",
|
|
"medium": "media",
|
|
"analysis": "analyses",
|
|
"crisis": "crises",
|
|
"status": "statuses",
|
|
}
|
|
|
|
if plural, ok := irregular[strings.ToLower(s)]; ok {
|
|
return plural
|
|
}
|
|
|
|
// Already plural (ends in 's' but not 'ss' or 'us')
|
|
if strings.HasSuffix(s, "s") && !strings.HasSuffix(s, "ss") && !strings.HasSuffix(s, "us") {
|
|
return s
|
|
}
|
|
|
|
// Words ending in s, x, z, ch, sh
|
|
if strings.HasSuffix(s, "s") || strings.HasSuffix(s, "x") ||
|
|
strings.HasSuffix(s, "z") || strings.HasSuffix(s, "ch") ||
|
|
strings.HasSuffix(s, "sh") {
|
|
return s + "es"
|
|
}
|
|
|
|
// Words ending in consonant + y
|
|
if len(s) >= 2 && strings.HasSuffix(s, "y") {
|
|
prevChar := s[len(s)-2]
|
|
if !isVowel(prevChar) {
|
|
return s[:len(s)-1] + "ies"
|
|
}
|
|
}
|
|
|
|
// Words ending in f or fe
|
|
if strings.HasSuffix(s, "f") {
|
|
return s[:len(s)-1] + "ves"
|
|
}
|
|
if strings.HasSuffix(s, "fe") {
|
|
return s[:len(s)-2] + "ves"
|
|
}
|
|
|
|
// Words ending in consonant + o
|
|
if len(s) >= 2 && strings.HasSuffix(s, "o") {
|
|
prevChar := s[len(s)-2]
|
|
if !isVowel(prevChar) {
|
|
return s + "es"
|
|
}
|
|
}
|
|
|
|
// Default: add 's'
|
|
return s + "s"
|
|
}
|
|
|
|
// Singularize converts a plural word to singular
|
|
// Basic implementation with common rules
|
|
func Singularize(s string) string {
|
|
if s == "" {
|
|
return ""
|
|
}
|
|
|
|
// Special cases
|
|
irregular := map[string]string{
|
|
"people": "person",
|
|
"children": "child",
|
|
"teeth": "tooth",
|
|
"feet": "foot",
|
|
"men": "man",
|
|
"women": "woman",
|
|
"mice": "mouse",
|
|
"geese": "goose",
|
|
"oxen": "ox",
|
|
"data": "datum",
|
|
"media": "medium",
|
|
"analyses": "analysis",
|
|
"crises": "crisis",
|
|
"statuses": "status",
|
|
}
|
|
|
|
if singular, ok := irregular[strings.ToLower(s)]; ok {
|
|
return singular
|
|
}
|
|
|
|
// Words ending in ies
|
|
if strings.HasSuffix(s, "ies") && len(s) > 3 {
|
|
return s[:len(s)-3] + "y"
|
|
}
|
|
|
|
// Words ending in ves
|
|
if strings.HasSuffix(s, "ves") {
|
|
return s[:len(s)-3] + "f"
|
|
}
|
|
|
|
// Words ending in ses, xes, zes, ches, shes
|
|
if strings.HasSuffix(s, "ses") || strings.HasSuffix(s, "xes") ||
|
|
strings.HasSuffix(s, "zes") || strings.HasSuffix(s, "ches") ||
|
|
strings.HasSuffix(s, "shes") {
|
|
return s[:len(s)-2]
|
|
}
|
|
|
|
// Words ending in s (not ss)
|
|
if strings.HasSuffix(s, "s") && !strings.HasSuffix(s, "ss") {
|
|
return s[:len(s)-1]
|
|
}
|
|
|
|
// Already singular
|
|
return s
|
|
}
|
|
|
|
// GeneratePrefix generates a 3-letter prefix from a table name
|
|
// Examples: process → PRO, mastertask → MTL, user → USR
|
|
func GeneratePrefix(tableName string) string {
|
|
if tableName == "" {
|
|
return "TBL"
|
|
}
|
|
|
|
// Remove common prefixes
|
|
tableName = strings.TrimPrefix(tableName, "tbl_")
|
|
tableName = strings.TrimPrefix(tableName, "tb_")
|
|
|
|
// Split by underscore and take first letters
|
|
parts := strings.Split(tableName, "_")
|
|
|
|
var prefix strings.Builder
|
|
for _, part := range parts {
|
|
if part == "" {
|
|
continue
|
|
}
|
|
prefix.WriteRune(unicode.ToUpper(rune(part[0])))
|
|
if prefix.Len() >= 3 {
|
|
break
|
|
}
|
|
}
|
|
|
|
result := prefix.String()
|
|
|
|
// If we don't have 3 letters yet, add more from the first part
|
|
if len(result) < 3 && len(parts) > 0 {
|
|
firstPart := parts[0]
|
|
for i := 1; i < len(firstPart) && len(result) < 3; i++ {
|
|
result += strings.ToUpper(string(firstPart[i]))
|
|
}
|
|
}
|
|
|
|
// Pad with 'X' if still too short
|
|
for len(result) < 3 {
|
|
result += "X"
|
|
}
|
|
|
|
return result[:3]
|
|
}
|
|
|
|
// isVowel checks if a byte is a vowel
|
|
func isVowel(c byte) bool {
|
|
c = byte(unicode.ToLower(rune(c)))
|
|
return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u'
|
|
}
|