Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / k8s.io / gengo / namer / namer.go
1 /*
2 Copyright 2015 The Kubernetes Authors.
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8     http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 package namer
18
19 import (
20         "path/filepath"
21         "strings"
22
23         "k8s.io/gengo/types"
24 )
25
26 const (
27         // GoSeperator is used to split go import paths.
28         // Forward slash is used instead of filepath.Seperator because it is the
29         // only universally-accepted path delimiter and the only delimiter not
30         // potentially forbidden by Go compilers. (In particular gc does not allow
31         // the use of backslashes in import paths.)
32         // See https://golang.org/ref/spec#Import_declarations.
33         // See also https://github.com/kubernetes/gengo/issues/83#issuecomment-367040772.
34         GoSeperator = "/"
35 )
36
37 // Returns whether a name is a private Go name.
38 func IsPrivateGoName(name string) bool {
39         return len(name) == 0 || strings.ToLower(name[:1]) == name[:1]
40 }
41
42 // NewPublicNamer is a helper function that returns a namer that makes
43 // CamelCase names. See the NameStrategy struct for an explanation of the
44 // arguments to this constructor.
45 func NewPublicNamer(prependPackageNames int, ignoreWords ...string) *NameStrategy {
46         n := &NameStrategy{
47                 Join:                Joiner(IC, IC),
48                 IgnoreWords:         map[string]bool{},
49                 PrependPackageNames: prependPackageNames,
50         }
51         for _, w := range ignoreWords {
52                 n.IgnoreWords[w] = true
53         }
54         return n
55 }
56
57 // NewPrivateNamer is a helper function that returns a namer that makes
58 // camelCase names. See the NameStrategy struct for an explanation of the
59 // arguments to this constructor.
60 func NewPrivateNamer(prependPackageNames int, ignoreWords ...string) *NameStrategy {
61         n := &NameStrategy{
62                 Join:                Joiner(IL, IC),
63                 IgnoreWords:         map[string]bool{},
64                 PrependPackageNames: prependPackageNames,
65         }
66         for _, w := range ignoreWords {
67                 n.IgnoreWords[w] = true
68         }
69         return n
70 }
71
72 // NewRawNamer will return a Namer that makes a name by which you would
73 // directly refer to a type, optionally keeping track of the import paths
74 // necessary to reference the names it provides. Tracker may be nil.
75 // The 'pkg' is the full package name, in which the Namer is used - all
76 // types from that package will be referenced by just type name without
77 // referencing the package.
78 //
79 // For example, if the type is map[string]int, a raw namer will literally
80 // return "map[string]int".
81 //
82 // Or if the type, in package foo, is "type Bar struct { ... }", then the raw
83 // namer will return "foo.Bar" as the name of the type, and if 'tracker' was
84 // not nil, will record that package foo needs to be imported.
85 func NewRawNamer(pkg string, tracker ImportTracker) *rawNamer {
86         return &rawNamer{pkg: pkg, tracker: tracker}
87 }
88
89 // Names is a map from Type to name, as defined by some Namer.
90 type Names map[*types.Type]string
91
92 // Namer takes a type, and assigns a name.
93 //
94 // The purpose of this complexity is so that you can assign coherent
95 // side-by-side systems of names for the types. For example, you might want a
96 // public interface, a private implementation struct, and also to reference
97 // literally the type name.
98 //
99 // Note that it is safe to call your own Name() function recursively to find
100 // the names of keys, elements, etc. This is because anonymous types can't have
101 // cycles in their names, and named types don't require the sort of recursion
102 // that would be problematic.
103 type Namer interface {
104         Name(*types.Type) string
105 }
106
107 // NameSystems is a map of a system name to a namer for that system.
108 type NameSystems map[string]Namer
109
110 // NameStrategy is a general Namer. The easiest way to use it is to copy the
111 // Public/PrivateNamer variables, and modify the members you wish to change.
112 //
113 // The Name method produces a name for the given type, of the forms:
114 // Anonymous types: <Prefix><Type description><Suffix>
115 // Named types: <Prefix><Optional Prepended Package name(s)><Original name><Suffix>
116 //
117 // In all cases, every part of the name is run through the capitalization
118 // functions.
119 //
120 // The IgnoreWords map can be set if you have directory names that are
121 // semantically meaningless for naming purposes, e.g. "proto".
122 //
123 // Prefix and Suffix can be used to disambiguate parallel systems of type
124 // names. For example, if you want to generate an interface and an
125 // implementation, you might want to suffix one with "Interface" and the other
126 // with "Implementation". Another common use-- if you want to generate private
127 // types, and one of your source types could be "string", you can't use the
128 // default lowercase private namer. You'll have to add a suffix or prefix.
129 type NameStrategy struct {
130         Prefix, Suffix string
131         Join           func(pre string, parts []string, post string) string
132
133         // Add non-meaningful package directory names here (e.g. "proto") and
134         // they will be ignored.
135         IgnoreWords map[string]bool
136
137         // If > 0, prepend exactly that many package directory names (or as
138         // many as there are).  Package names listed in "IgnoreWords" will be
139         // ignored.
140         //
141         // For example, if Ignore words lists "proto" and type Foo is in
142         // pkg/server/frobbing/proto, then a value of 1 will give a type name
143         // of FrobbingFoo, 2 gives ServerFrobbingFoo, etc.
144         PrependPackageNames int
145
146         // A cache of names thus far assigned by this namer.
147         Names
148 }
149
150 // IC ensures the first character is uppercase.
151 func IC(in string) string {
152         if in == "" {
153                 return in
154         }
155         return strings.ToUpper(in[:1]) + in[1:]
156 }
157
158 // IL ensures the first character is lowercase.
159 func IL(in string) string {
160         if in == "" {
161                 return in
162         }
163         return strings.ToLower(in[:1]) + in[1:]
164 }
165
166 // Joiner lets you specify functions that preprocess the various components of
167 // a name before joining them. You can construct e.g. camelCase or CamelCase or
168 // any other way of joining words. (See the IC and IL convenience functions.)
169 func Joiner(first, others func(string) string) func(pre string, in []string, post string) string {
170         return func(pre string, in []string, post string) string {
171                 tmp := []string{others(pre)}
172                 for i := range in {
173                         tmp = append(tmp, others(in[i]))
174                 }
175                 tmp = append(tmp, others(post))
176                 return first(strings.Join(tmp, ""))
177         }
178 }
179
180 func (ns *NameStrategy) removePrefixAndSuffix(s string) string {
181         // The join function may have changed capitalization.
182         lowerIn := strings.ToLower(s)
183         lowerP := strings.ToLower(ns.Prefix)
184         lowerS := strings.ToLower(ns.Suffix)
185         b, e := 0, len(s)
186         if strings.HasPrefix(lowerIn, lowerP) {
187                 b = len(ns.Prefix)
188         }
189         if strings.HasSuffix(lowerIn, lowerS) {
190                 e -= len(ns.Suffix)
191         }
192         return s[b:e]
193 }
194
195 var (
196         importPathNameSanitizer = strings.NewReplacer("-", "_", ".", "")
197 )
198
199 // filters out unwanted directory names and sanitizes remaining names.
200 func (ns *NameStrategy) filterDirs(path string) []string {
201         allDirs := strings.Split(path, GoSeperator)
202         dirs := make([]string, 0, len(allDirs))
203         for _, p := range allDirs {
204                 if ns.IgnoreWords == nil || !ns.IgnoreWords[p] {
205                         dirs = append(dirs, importPathNameSanitizer.Replace(p))
206                 }
207         }
208         return dirs
209 }
210
211 // See the comment on NameStrategy.
212 func (ns *NameStrategy) Name(t *types.Type) string {
213         if ns.Names == nil {
214                 ns.Names = Names{}
215         }
216         if s, ok := ns.Names[t]; ok {
217                 return s
218         }
219
220         if t.Name.Package != "" {
221                 dirs := append(ns.filterDirs(t.Name.Package), t.Name.Name)
222                 i := ns.PrependPackageNames + 1
223                 dn := len(dirs)
224                 if i > dn {
225                         i = dn
226                 }
227                 name := ns.Join(ns.Prefix, dirs[dn-i:], ns.Suffix)
228                 ns.Names[t] = name
229                 return name
230         }
231
232         // Only anonymous types remain.
233         var name string
234         switch t.Kind {
235         case types.Builtin:
236                 name = ns.Join(ns.Prefix, []string{t.Name.Name}, ns.Suffix)
237         case types.Map:
238                 name = ns.Join(ns.Prefix, []string{
239                         "Map",
240                         ns.removePrefixAndSuffix(ns.Name(t.Key)),
241                         "To",
242                         ns.removePrefixAndSuffix(ns.Name(t.Elem)),
243                 }, ns.Suffix)
244         case types.Slice:
245                 name = ns.Join(ns.Prefix, []string{
246                         "Slice",
247                         ns.removePrefixAndSuffix(ns.Name(t.Elem)),
248                 }, ns.Suffix)
249         case types.Pointer:
250                 name = ns.Join(ns.Prefix, []string{
251                         "Pointer",
252                         ns.removePrefixAndSuffix(ns.Name(t.Elem)),
253                 }, ns.Suffix)
254         case types.Struct:
255                 names := []string{"Struct"}
256                 for _, m := range t.Members {
257                         names = append(names, ns.removePrefixAndSuffix(ns.Name(m.Type)))
258                 }
259                 name = ns.Join(ns.Prefix, names, ns.Suffix)
260         case types.Chan:
261                 name = ns.Join(ns.Prefix, []string{
262                         "Chan",
263                         ns.removePrefixAndSuffix(ns.Name(t.Elem)),
264                 }, ns.Suffix)
265         case types.Interface:
266                 // TODO: add to name test
267                 names := []string{"Interface"}
268                 for _, m := range t.Methods {
269                         // TODO: include function signature
270                         names = append(names, m.Name.Name)
271                 }
272                 name = ns.Join(ns.Prefix, names, ns.Suffix)
273         case types.Func:
274                 // TODO: add to name test
275                 parts := []string{"Func"}
276                 for _, pt := range t.Signature.Parameters {
277                         parts = append(parts, ns.removePrefixAndSuffix(ns.Name(pt)))
278                 }
279                 parts = append(parts, "Returns")
280                 for _, rt := range t.Signature.Results {
281                         parts = append(parts, ns.removePrefixAndSuffix(ns.Name(rt)))
282                 }
283                 name = ns.Join(ns.Prefix, parts, ns.Suffix)
284         default:
285                 name = "unnameable_" + string(t.Kind)
286         }
287         ns.Names[t] = name
288         return name
289 }
290
291 // ImportTracker allows a raw namer to keep track of the packages needed for
292 // import. You can implement yourself or use the one in the generation package.
293 type ImportTracker interface {
294         AddType(*types.Type)
295         LocalNameOf(packagePath string) string
296         PathOf(localName string) (string, bool)
297         ImportLines() []string
298 }
299
300 type rawNamer struct {
301         pkg     string
302         tracker ImportTracker
303         Names
304 }
305
306 // Name makes a name the way you'd write it to literally refer to type t,
307 // making ordinary assumptions about how you've imported t's package (or using
308 // r.tracker to specifically track the package imports).
309 func (r *rawNamer) Name(t *types.Type) string {
310         if r.Names == nil {
311                 r.Names = Names{}
312         }
313         if name, ok := r.Names[t]; ok {
314                 return name
315         }
316         if t.Name.Package != "" {
317                 var name string
318                 if r.tracker != nil {
319                         r.tracker.AddType(t)
320                         if t.Name.Package == r.pkg {
321                                 name = t.Name.Name
322                         } else {
323                                 name = r.tracker.LocalNameOf(t.Name.Package) + "." + t.Name.Name
324                         }
325                 } else {
326                         if t.Name.Package == r.pkg {
327                                 name = t.Name.Name
328                         } else {
329                                 name = filepath.Base(t.Name.Package) + "." + t.Name.Name
330                         }
331                 }
332                 r.Names[t] = name
333                 return name
334         }
335         var name string
336         switch t.Kind {
337         case types.Builtin:
338                 name = t.Name.Name
339         case types.Map:
340                 name = "map[" + r.Name(t.Key) + "]" + r.Name(t.Elem)
341         case types.Slice:
342                 name = "[]" + r.Name(t.Elem)
343         case types.Pointer:
344                 name = "*" + r.Name(t.Elem)
345         case types.Struct:
346                 elems := []string{}
347                 for _, m := range t.Members {
348                         elems = append(elems, m.Name+" "+r.Name(m.Type))
349                 }
350                 name = "struct{" + strings.Join(elems, "; ") + "}"
351         case types.Chan:
352                 // TODO: include directionality
353                 name = "chan " + r.Name(t.Elem)
354         case types.Interface:
355                 // TODO: add to name test
356                 elems := []string{}
357                 for _, m := range t.Methods {
358                         // TODO: include function signature
359                         elems = append(elems, m.Name.Name)
360                 }
361                 name = "interface{" + strings.Join(elems, "; ") + "}"
362         case types.Func:
363                 // TODO: add to name test
364                 params := []string{}
365                 for _, pt := range t.Signature.Parameters {
366                         params = append(params, r.Name(pt))
367                 }
368                 results := []string{}
369                 for _, rt := range t.Signature.Results {
370                         results = append(results, r.Name(rt))
371                 }
372                 name = "func(" + strings.Join(params, ",") + ")"
373                 if len(results) == 1 {
374                         name += " " + results[0]
375                 } else if len(results) > 1 {
376                         name += " (" + strings.Join(results, ",") + ")"
377                 }
378         default:
379                 name = "unnameable_" + string(t.Kind)
380         }
381         r.Names[t] = name
382         return name
383 }