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 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
|
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package mangling
import (
"sort"
"strings"
"unicode"
"unicode/utf8"
)
// DefaultInitialisms returns all the initialisms configured by default for this package.
//
// # Motivation
//
// Common initialisms are acronyms for which the ordinary camel-casing rules are altered and
// for which we retain the original case.
//
// This is largely specific to the go naming conventions enforced by golint (now revive).
//
// # Example
//
// In go, "id" is a good-looking identifier, but "Id" is not and "ID" is preferred
// (notice that this stems only from conventions: the go compiler accepts all of these).
//
// Similarly, we may use "http", but not "Http". In this case, "HTTP" is preferred.
//
// # Reference and customization
//
// The default list of these casing-style exceptions is taken from the [github.com/mgechev/revive] linter for go:
// https://github.com/mgechev/revive/blob/master/lint/name.go#L93
//
// There are a few additions to the original list, such as IPv4, IPv6 and OAI ("OpenAPI").
//
// For these additions, "IPv4" would be preferred to "Ipv4" or "IPV4", and "OAI" to "Oai"
//
// You may redefine this list entirely using the mangler option [WithInitialisms], or simply add extra definitions
// using [WithAdditionalInitialisms].
//
// # Mixed-case and plurals
//
// Notice that initialisms are not necessarily fully upper-cased: a mixed-case initialism indicates the preferred casing.
//
// Obviously, lower-case only initialisms do not make a lot of sense: if lower-case only initialisms are added,
// they will be considered fully capitalized.
//
// Plural forms use mixed case like "IDs". And so do values like "IPv4" or "IPv6".
//
// The [NameMangler] automatically detects simple plurals for words such as "IDs" or "APIs",
// so you don't need to configure these variants.
//
// At this moment, it doesn't support pluralization of terms that ends with an 's' (or 'S'), since there is
// no clear consensus on whether a word like DNS should be pluralized as DNSes or remain invariant.
// The [NameMangler] consider those invariant. Therefore DNSs or DNSes are not recognized as plurals for DNS.
//
// Besids, we don't want to support pluralization of terms which would otherwise conflict with another one,
// like "HTTPs" vs "HTTPS". All these should be considered invariant. Hence: "Https" matches "HTTPS" and
// "HTTPSS" is "HTTPS" followed by "S".
func DefaultInitialisms() []string {
return []string{
"ACL",
"API",
"ASCII",
"CPU",
"CSS",
"DNS",
"EOF",
"GUID",
"HTML",
"HTTPS",
"HTTP",
"ID",
"IP",
"IPv4", // prefer the mixed case outcome IPv4 over the capitalized IPV4
"IPv6", // prefer the mixed case outcome IPv6 over the capitalized IPV6
"JSON",
"LHS",
"OAI",
"QPS",
"RAM",
"RHS",
"RPC",
"SLA",
"SMTP",
"SQL",
"SSH",
"TCP",
"TLS",
"TTL",
"UDP",
"UI",
"UID",
"UUID",
"URI",
"URL",
"UTF8",
"VM",
"XML",
"XMPP",
"XSRF",
"XSS",
}
}
type indexOfInitialisms struct {
initialismsCache
index map[string]struct{}
}
func newIndexOfInitialisms() *indexOfInitialisms {
return &indexOfInitialisms{
index: make(map[string]struct{}),
}
}
func (m *indexOfInitialisms) add(words ...string) *indexOfInitialisms {
for _, word := range words {
// sanitization of injected words: trimmed from blanks, and must start with a letter
trimmed := strings.TrimSpace(word)
firstRune, _ := utf8.DecodeRuneInString(trimmed)
if !unicode.IsLetter(firstRune) {
continue
}
// Initialisms are case-sensitive. This means that we support mixed-case words.
// However, if specified as a lower-case string, the initialism should be fully capitalized.
if trimmed == strings.ToLower(trimmed) {
m.index[strings.ToUpper(trimmed)] = struct{}{}
continue
}
m.index[trimmed] = struct{}{}
}
return m
}
func (m *indexOfInitialisms) sorted() []string {
result := make([]string, 0, len(m.index))
for k := range m.index {
result = append(result, k)
}
sort.Sort(sort.Reverse(byInitialism(result)))
return result
}
func (m *indexOfInitialisms) buildCache() {
m.build(m.sorted(), m.pluralForm)
}
// initialismsCache caches all needed pre-computed and converted initialism entries,
// in the desired resolution order.
type initialismsCache struct {
initialisms []string
initialismsRunes [][]rune
initialismsUpperCased [][]rune // initialisms cached in their trimmed, upper-cased version
initialismsPluralForm []pluralForm
}
func (c *initialismsCache) build(in []string, pluralfunc func(string) pluralForm) {
c.initialisms = in
c.initialismsRunes = asRunes(c.initialisms)
c.initialismsUpperCased = asUpperCased(c.initialisms)
c.initialismsPluralForm = asPluralForms(c.initialisms, pluralfunc)
}
// pluralForm denotes the kind of pluralization to be used for initialisms.
//
// At this moment, initialisms are either invariant or follow a simple plural form with an
// extra (lower case) "s".
type pluralForm uint8
const (
notPlural pluralForm = iota
invariantPlural
simplePlural
)
// pluralForm indicates how we want to pluralize a given initialism.
//
// Besides configured invariant forms (like HTTP and HTTPS),
// an initialism is normally pluralized by adding a single 's', like in IDs.
//
// Initialisms ending with an 'S' or an 's' are configured as invariant (we don't
// support plural forms like CSSes or DNSes, however the mechanism could be extended to
// do just that).
func (m *indexOfInitialisms) pluralForm(key string) pluralForm {
if _, ok := m.index[key]; !ok {
return notPlural
}
if strings.HasSuffix(strings.ToUpper(key), "S") {
return invariantPlural
}
if _, ok := m.index[key+"s"]; ok {
return invariantPlural
}
if _, ok := m.index[key+"S"]; ok {
return invariantPlural
}
return simplePlural
}
type byInitialism []string
func (s byInitialism) Len() int {
return len(s)
}
func (s byInitialism) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// Less specifies the order in which initialisms are prioritized:
// 1. match longest first
// 2. when equal length, match in reverse lexicographical order, lower case match comes first
func (s byInitialism) Less(i, j int) bool {
if len(s[i]) != len(s[j]) {
return len(s[i]) < len(s[j])
}
return s[i] < s[j]
}
func asRunes(in []string) [][]rune {
out := make([][]rune, len(in))
for i, initialism := range in {
out[i] = []rune(initialism)
}
return out
}
func asUpperCased(in []string) [][]rune {
out := make([][]rune, len(in))
for i, initialism := range in {
out[i] = []rune(upper(trim(initialism)))
}
return out
}
// asPluralForms bakes an index of pluralization support.
func asPluralForms(in []string, pluralFunc func(string) pluralForm) []pluralForm {
out := make([]pluralForm, len(in))
for i, initialism := range in {
out[i] = pluralFunc(initialism)
}
return out
}
|