So far so good
This commit is contained in:
284
pkg/writers/gorm/name_converter.go
Normal file
284
pkg/writers/gorm/name_converter.go
Normal file
@@ -0,0 +1,284 @@
|
||||
package gorm
|
||||
|
||||
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 == false && 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'
|
||||
}
|
||||
Reference in New Issue
Block a user