namer.go 12 KB

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