namer.go 11 KB

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