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' }