podman
262 строки · 6.2 Кб
1// Copyright 2015 go-swagger maintainers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package swag
16
17import (
18"unicode"
19)
20
21var nameReplaceTable = map[rune]string{
22'@': "At ",
23'&': "And ",
24'|': "Pipe ",
25'$': "Dollar ",
26'!': "Bang ",
27'-': "",
28'_': "",
29}
30
31type (
32splitter struct {
33postSplitInitialismCheck bool
34initialisms []string
35}
36
37splitterOption func(*splitter) *splitter
38)
39
40// split calls the splitter; splitter provides more control and post options
41func split(str string) []string {
42lexems := newSplitter().split(str)
43result := make([]string, 0, len(lexems))
44
45for _, lexem := range lexems {
46result = append(result, lexem.GetOriginal())
47}
48
49return result
50
51}
52
53func (s *splitter) split(str string) []nameLexem {
54return s.toNameLexems(str)
55}
56
57func newSplitter(options ...splitterOption) *splitter {
58splitter := &splitter{
59postSplitInitialismCheck: false,
60initialisms: initialisms,
61}
62
63for _, option := range options {
64splitter = option(splitter)
65}
66
67return splitter
68}
69
70// withPostSplitInitialismCheck allows to catch initialisms after main split process
71func withPostSplitInitialismCheck(s *splitter) *splitter {
72s.postSplitInitialismCheck = true
73return s
74}
75
76type (
77initialismMatch struct {
78start, end int
79body []rune
80complete bool
81}
82initialismMatches []*initialismMatch
83)
84
85func (s *splitter) toNameLexems(name string) []nameLexem {
86nameRunes := []rune(name)
87matches := s.gatherInitialismMatches(nameRunes)
88return s.mapMatchesToNameLexems(nameRunes, matches)
89}
90
91func (s *splitter) gatherInitialismMatches(nameRunes []rune) initialismMatches {
92matches := make(initialismMatches, 0)
93
94for currentRunePosition, currentRune := range nameRunes {
95newMatches := make(initialismMatches, 0, len(matches))
96
97// check current initialism matches
98for _, match := range matches {
99if keepCompleteMatch := match.complete; keepCompleteMatch {
100newMatches = append(newMatches, match)
101continue
102}
103
104// drop failed match
105currentMatchRune := match.body[currentRunePosition-match.start]
106if !s.initialismRuneEqual(currentMatchRune, currentRune) {
107continue
108}
109
110// try to complete ongoing match
111if currentRunePosition-match.start == len(match.body)-1 {
112// we are close; the next step is to check the symbol ahead
113// if it is a small letter, then it is not the end of match
114// but beginning of the next word
115
116if currentRunePosition < len(nameRunes)-1 {
117nextRune := nameRunes[currentRunePosition+1]
118if newWord := unicode.IsLower(nextRune); newWord {
119// oh ok, it was the start of a new word
120continue
121}
122}
123
124match.complete = true
125match.end = currentRunePosition
126}
127
128newMatches = append(newMatches, match)
129}
130
131// check for new initialism matches
132for _, initialism := range s.initialisms {
133initialismRunes := []rune(initialism)
134if s.initialismRuneEqual(initialismRunes[0], currentRune) {
135newMatches = append(newMatches, &initialismMatch{
136start: currentRunePosition,
137body: initialismRunes,
138complete: false,
139})
140}
141}
142
143matches = newMatches
144}
145
146return matches
147}
148
149func (s *splitter) mapMatchesToNameLexems(nameRunes []rune, matches initialismMatches) []nameLexem {
150nameLexems := make([]nameLexem, 0)
151
152var lastAcceptedMatch *initialismMatch
153for _, match := range matches {
154if !match.complete {
155continue
156}
157
158if firstMatch := lastAcceptedMatch == nil; firstMatch {
159nameLexems = append(nameLexems, s.breakCasualString(nameRunes[:match.start])...)
160nameLexems = append(nameLexems, s.breakInitialism(string(match.body)))
161
162lastAcceptedMatch = match
163
164continue
165}
166
167if overlappedMatch := match.start <= lastAcceptedMatch.end; overlappedMatch {
168continue
169}
170
171middle := nameRunes[lastAcceptedMatch.end+1 : match.start]
172nameLexems = append(nameLexems, s.breakCasualString(middle)...)
173nameLexems = append(nameLexems, s.breakInitialism(string(match.body)))
174
175lastAcceptedMatch = match
176}
177
178// we have not found any accepted matches
179if lastAcceptedMatch == nil {
180return s.breakCasualString(nameRunes)
181}
182
183if lastAcceptedMatch.end+1 != len(nameRunes) {
184rest := nameRunes[lastAcceptedMatch.end+1:]
185nameLexems = append(nameLexems, s.breakCasualString(rest)...)
186}
187
188return nameLexems
189}
190
191func (s *splitter) initialismRuneEqual(a, b rune) bool {
192return a == b
193}
194
195func (s *splitter) breakInitialism(original string) nameLexem {
196return newInitialismNameLexem(original, original)
197}
198
199func (s *splitter) breakCasualString(str []rune) []nameLexem {
200segments := make([]nameLexem, 0)
201currentSegment := ""
202
203addCasualNameLexem := func(original string) {
204segments = append(segments, newCasualNameLexem(original))
205}
206
207addInitialismNameLexem := func(original, match string) {
208segments = append(segments, newInitialismNameLexem(original, match))
209}
210
211addNameLexem := func(original string) {
212if s.postSplitInitialismCheck {
213for _, initialism := range s.initialisms {
214if upper(initialism) == upper(original) {
215addInitialismNameLexem(original, initialism)
216return
217}
218}
219}
220
221addCasualNameLexem(original)
222}
223
224for _, rn := range string(str) {
225if replace, found := nameReplaceTable[rn]; found {
226if currentSegment != "" {
227addNameLexem(currentSegment)
228currentSegment = ""
229}
230
231if replace != "" {
232addNameLexem(replace)
233}
234
235continue
236}
237
238if !unicode.In(rn, unicode.L, unicode.M, unicode.N, unicode.Pc) {
239if currentSegment != "" {
240addNameLexem(currentSegment)
241currentSegment = ""
242}
243
244continue
245}
246
247if unicode.IsUpper(rn) {
248if currentSegment != "" {
249addNameLexem(currentSegment)
250}
251currentSegment = ""
252}
253
254currentSegment += string(rn)
255}
256
257if currentSegment != "" {
258addNameLexem(currentSegment)
259}
260
261return segments
262}
263