First working version
This commit is contained in:
227
internal/naming/naming.go
Normal file
227
internal/naming/naming.go
Normal file
@ -0,0 +1,227 @@
|
||||
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' and not special case)
|
||||
if strings.HasSuffix(lower, "s") && !strings.HasSuffix(lower, "us") {
|
||||
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, "_")
|
||||
}
|
||||
Reference in New Issue
Block a user