232 lines
5.6 KiB
Go
232 lines
5.6 KiB
Go
package naming
|
|
|
|
import (
|
|
"strings"
|
|
"unicode"
|
|
)
|
|
|
|
// ToSnakeCase converts a string to snake_case
|
|
func ToSnakeCase(s string) string {
|
|
var result []rune
|
|
for i, r := range s {
|
|
if unicode.IsUpper(r) {
|
|
if i > 0 {
|
|
result = append(result, '_')
|
|
}
|
|
result = append(result, unicode.ToLower(r))
|
|
} else {
|
|
result = append(result, r)
|
|
}
|
|
}
|
|
return string(result)
|
|
}
|
|
|
|
// ToPascalCase converts snake_case to PascalCase
|
|
func ToPascalCase(s string) string {
|
|
parts := strings.Split(s, "_")
|
|
for i, part := range parts {
|
|
if len(part) > 0 {
|
|
parts[i] = strings.ToUpper(part[:1]) + part[1:]
|
|
}
|
|
}
|
|
return strings.Join(parts, "")
|
|
}
|
|
|
|
// Singularize converts a plural word to singular
|
|
// Uses common English pluralization rules
|
|
func Singularize(word string) string {
|
|
// Handle empty string
|
|
if word == "" {
|
|
return word
|
|
}
|
|
|
|
// Common irregular plurals
|
|
irregulars := map[string]string{
|
|
"people": "person",
|
|
"men": "man",
|
|
"women": "woman",
|
|
"children": "child",
|
|
"teeth": "tooth",
|
|
"feet": "foot",
|
|
"mice": "mouse",
|
|
"geese": "goose",
|
|
"oxen": "ox",
|
|
"sheep": "sheep",
|
|
"fish": "fish",
|
|
"deer": "deer",
|
|
"series": "series",
|
|
"species": "species",
|
|
"quizzes": "quiz",
|
|
"analyses": "analysis",
|
|
"diagnoses": "diagnosis",
|
|
"oases": "oasis",
|
|
"theses": "thesis",
|
|
"crises": "crisis",
|
|
"phenomena": "phenomenon",
|
|
"criteria": "criterion",
|
|
"data": "datum",
|
|
}
|
|
|
|
lower := strings.ToLower(word)
|
|
if singular, ok := irregulars[lower]; ok {
|
|
return preserveCase(word, singular)
|
|
}
|
|
|
|
// Handle words ending in 'ies' -> 'y'
|
|
if strings.HasSuffix(lower, "ies") && len(word) > 3 {
|
|
return word[:len(word)-3] + "y"
|
|
}
|
|
|
|
// Handle words ending in 'ves' -> 'fe' or 'f'
|
|
if strings.HasSuffix(lower, "ves") && len(word) > 3 {
|
|
base := word[:len(word)-3]
|
|
// Common words that end in 'fe'
|
|
if strings.HasSuffix(strings.ToLower(base), "li") ||
|
|
strings.HasSuffix(strings.ToLower(base), "wi") ||
|
|
strings.HasSuffix(strings.ToLower(base), "kni") {
|
|
return base + "fe"
|
|
}
|
|
return base + "f"
|
|
}
|
|
|
|
// Handle words ending in 'xes', 'ses', 'shes', 'ches' -> remove 'es'
|
|
if strings.HasSuffix(lower, "xes") ||
|
|
strings.HasSuffix(lower, "ses") ||
|
|
strings.HasSuffix(lower, "shes") ||
|
|
strings.HasSuffix(lower, "ches") {
|
|
if len(word) > 2 {
|
|
return word[:len(word)-2]
|
|
}
|
|
}
|
|
|
|
// Handle words ending in 'oes' -> 'o'
|
|
if strings.HasSuffix(lower, "oes") && len(word) > 3 {
|
|
return word[:len(word)-2]
|
|
}
|
|
|
|
// Handle simple 's' suffix
|
|
if strings.HasSuffix(lower, "s") && len(word) > 1 {
|
|
// Don't remove 's' from words that naturally end in 's'
|
|
if !strings.HasSuffix(lower, "ss") &&
|
|
!strings.HasSuffix(lower, "us") &&
|
|
!strings.HasSuffix(lower, "is") {
|
|
return word[:len(word)-1]
|
|
}
|
|
}
|
|
|
|
return word
|
|
}
|
|
|
|
// Pluralize converts a singular word to plural
|
|
func Pluralize(word string) string {
|
|
if word == "" {
|
|
return word
|
|
}
|
|
|
|
// Common irregular plurals
|
|
irregulars := map[string]string{
|
|
"person": "people",
|
|
"man": "men",
|
|
"woman": "women",
|
|
"child": "children",
|
|
"tooth": "teeth",
|
|
"foot": "feet",
|
|
"mouse": "mice",
|
|
"goose": "geese",
|
|
"ox": "oxen",
|
|
"sheep": "sheep",
|
|
"fish": "fish",
|
|
"deer": "deer",
|
|
"series": "series",
|
|
"species": "species",
|
|
"quiz": "quizzes",
|
|
"analysis": "analyses",
|
|
"diagnosis": "diagnoses",
|
|
"oasis": "oases",
|
|
"thesis": "theses",
|
|
"crisis": "crises",
|
|
"phenomenon": "phenomena",
|
|
"criterion": "criteria",
|
|
"datum": "data",
|
|
}
|
|
|
|
lower := strings.ToLower(word)
|
|
if plural, ok := irregulars[lower]; ok {
|
|
return preserveCase(word, plural)
|
|
}
|
|
|
|
// Already plural (ends in 's' after a consonant, but not 'ss', 'us', 'is')
|
|
// Skip this check if word ends in 'ss' (like 'class', 'glass')
|
|
if strings.HasSuffix(lower, "s") &&
|
|
!strings.HasSuffix(lower, "ss") &&
|
|
!strings.HasSuffix(lower, "us") &&
|
|
!strings.HasSuffix(lower, "is") {
|
|
return word
|
|
}
|
|
|
|
// Handle words ending in 'y' -> 'ies'
|
|
if strings.HasSuffix(lower, "y") && len(word) > 1 {
|
|
prevChar := rune(lower[len(lower)-2])
|
|
if !isVowel(prevChar) {
|
|
return word[:len(word)-1] + "ies"
|
|
}
|
|
}
|
|
|
|
// Handle words ending in 'f' or 'fe' -> 'ves'
|
|
if strings.HasSuffix(lower, "f") {
|
|
return word[:len(word)-1] + "ves"
|
|
}
|
|
if strings.HasSuffix(lower, "fe") {
|
|
return word[:len(word)-2] + "ves"
|
|
}
|
|
|
|
// Handle words ending in 'o' -> 'oes'
|
|
if strings.HasSuffix(lower, "o") && len(word) > 1 {
|
|
prevChar := rune(lower[len(lower)-2])
|
|
if !isVowel(prevChar) {
|
|
return word + "es"
|
|
}
|
|
}
|
|
|
|
// Handle words ending in 'x', 's', 'sh', 'ch' -> add 'es'
|
|
if strings.HasSuffix(lower, "x") ||
|
|
strings.HasSuffix(lower, "s") ||
|
|
strings.HasSuffix(lower, "sh") ||
|
|
strings.HasSuffix(lower, "ch") {
|
|
return word + "es"
|
|
}
|
|
|
|
// Default: just add 's'
|
|
return word + "s"
|
|
}
|
|
|
|
// preserveCase preserves the case pattern of the original word
|
|
func preserveCase(original, replacement string) string {
|
|
if len(original) == 0 {
|
|
return replacement
|
|
}
|
|
|
|
if unicode.IsUpper(rune(original[0])) {
|
|
return strings.ToUpper(replacement[:1]) + replacement[1:]
|
|
}
|
|
return replacement
|
|
}
|
|
|
|
// isVowel checks if a rune is a vowel
|
|
func isVowel(r rune) bool {
|
|
vowels := "aeiouAEIOU"
|
|
return strings.ContainsRune(vowels, r)
|
|
}
|
|
|
|
// SingularizeTableName converts a table name to its singular form
|
|
// Handles snake_case table names
|
|
func SingularizeTableName(tableName string) string {
|
|
parts := strings.Split(tableName, "_")
|
|
if len(parts) > 0 {
|
|
// Only singularize the last part
|
|
lastIdx := len(parts) - 1
|
|
parts[lastIdx] = Singularize(parts[lastIdx])
|
|
}
|
|
return strings.Join(parts, "_")
|
|
}
|