First working version

This commit is contained in:
Eden Kirin
2025-10-31 14:36:41 +01:00
parent f8edfc0fc1
commit f9f67b6c93
22 changed files with 2277 additions and 0 deletions

227
internal/naming/naming.go Normal file
View 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, "_")
}