1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
|
package strcase
import (
"strings"
"unicode"
)
// ToPrivateGoName returns the Go public name of the given string.
func ToPublicGoName(s string) string {
return toGoName(TitleFirstWord(s))
}
// ToPrivateGoName returns the Go private name of the given string.
func ToPrivateGoName(s string) string {
return toGoName(lowerCaseFirstLetterOrAcronyms(s))
}
// toGoName returns a different name if it should be different.
func toGoName(name string) (should string) {
name = strings.ReplaceAll(name, " ", "_")
name = strings.ReplaceAll(name, "-", "_")
// Fast path for simple cases: "_" and all lowercase.
if name == "_" {
return name
}
allLower := true
for _, r := range name {
if !unicode.IsLower(r) {
allLower = false
break
}
}
if allLower {
return name
}
// Split camelCase at any lower->upper transition, and split on underscores.
// Check each word for common initialisms.
runes := []rune(name)
w, i := 0, 0 // index of start of word, scan
for i+1 <= len(runes) {
eow := false // whether we hit the end of a word
switch {
case i+1 == len(runes):
eow = true
case runes[i+1] == '_':
eow = true
n := 1
for i+n+1 < len(runes) && runes[i+n+1] == '_' {
n++
}
if i+n+1 < len(runes) && unicode.IsDigit(runes[i]) && unicode.IsDigit(runes[i+n+1]) {
n--
}
copy(runes[i+1:], runes[i+n+1:])
runes = runes[:len(runes)-n]
case unicode.IsLower(runes[i]) && !unicode.IsLower(runes[i+1]):
eow = true
}
i++
if !eow {
continue
}
// [w,i) is a word.
word := string(runes[w:i])
u := strings.ToUpper(word)
if commonInitialisms[u] {
// Keep consistent case, which is lowercase only at the start.
if w == 0 && unicode.IsLower(runes[w]) {
u = strings.ToLower(u)
}
// All the common initialisms are ASCII,
// so we can replace the bytes exactly.
copy(runes[w:], []rune(u))
} else if specialCase, exist := customInitialisms[u]; exist {
if w == 0 && unicode.IsLower(runes[w]) {
u = specialCase[1]
} else {
u = specialCase[0]
}
copy(runes[w:], []rune(u))
} else if w > 0 && strings.ToLower(word) == word {
// already all lowercase, and not the first word, so uppercase the first character.
runes[w] = unicode.ToUpper(runes[w])
}
w = i
}
return string(runes)
}
// Deprecated: this list should not be completed as it affects generation for our Go SDK only.
//
// commonInitialisms is a set of common initialisms.
// Only add entries that are highly unlikely to be non-initialisms.
// For instance, "ID" is fine (Freudian code is rare), but "AND" is not.
var commonInitialisms = map[string]bool{
"ACL": true,
"API": true,
"ASCII": true,
"CPU": true,
"CSS": true,
"DHCP": true,
"DNS": true,
"EOF": true,
"GUID": true,
"HTML": true,
"HTTP": true,
"HTTPS": true,
"ID": true,
"IP": true,
"JSON": true,
"LB": true,
"LHS": true,
"QPS": true,
"RAM": true,
"RHS": true,
"RPC": true,
"SLA": true,
"SMTP": true,
"SQL": true,
"SSD": true,
"SSH": true,
"TCP": true,
"TLS": true,
"TTL": true,
"UDP": true,
"UI": true,
"UID": true,
"UUID": true,
"URI": true,
"URL": true,
"UTF8": true,
"VM": true,
"XML": true,
"XMPP": true,
"XSRF": true,
"XSS": true,
}
// customInitialisms is a set of common initialisms we use at Scaleway.
// value[0] is the uppercase replacement
// value[1] is the lowercase replacement
var customInitialisms = map[string][2]string{
"ACLS": {"ACLs", "acls"},
"APIS": {"APIs", "apis"},
"CPUS": {"CPUs", "cpus"},
"IDS": {"IDs", "ids"},
"IPS": {"IPs", "ips"},
"IPV": {"IPv", "ipv"}, // handle IPV4 && IPV6
"LBS": {"LBs", "lbs"},
"UIDS": {"UIDs", "uids"},
"UUIDS": {"UUIDs", "uuids"},
"URIS": {"URIs", "uris"},
"URLS": {"URLs", "urls"},
}
// TitleFirstWord upper case the first letter of a string.
func TitleFirstWord(s string) string {
if s == "" {
return s
}
r := []rune(s)
r[0] = unicode.ToUpper(r[0])
return string(r)
}
// UntitleFirstWord lower case the first letter of a string.
func UntitleFirstWord(s string) string {
if s == "" {
return s
}
r := []rune(s)
firstWord := strings.Split(s, " ")[0]
_, isCommonInitialism := commonInitialisms[firstWord]
_, isCustomInitialism := customInitialisms[firstWord]
if !isCommonInitialism && !isCustomInitialism {
r[0] = unicode.ToLower(r[0])
}
return string(r)
}
// lowerCaseFirstLetterOrAcronyms lower case the first letter of a string.
func lowerCaseFirstLetterOrAcronyms(s string) string {
r := []rune(s)
if len(r) == 0 {
return ""
}
for i := 0; len(r) > i && unicode.IsUpper(r[i]); i++ {
word := string(r[:i+1])
if u := strings.ToUpper(word); commonInitialisms[u] {
copy(r[0:], []rune(strings.ToLower(u)))
break
}
}
r[0] = unicode.ToLower(r[0])
return string(r)
}
|