File: initialism_index.go

package info (click to toggle)
golang-github-go-openapi-swag 1%3A0.25.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,200 kB
  • sloc: sh: 30; makefile: 3
file content (268 lines) | stat: -rw-r--r-- 7,433 bytes parent folder | download
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
}