generator.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. package generator
  2. import (
  3. "bytes"
  4. "io"
  5. "go-common/app/tool/gengo/namer"
  6. "go-common/app/tool/gengo/parser"
  7. "go-common/app/tool/gengo/types"
  8. )
  9. // Package contains the contract for generating a package.
  10. type Package interface {
  11. // Name returns the package short name.
  12. Name() string
  13. // Path returns the package import path.
  14. Path() string
  15. // Filter should return true if this package cares about this type.
  16. // Otherwise, this type will be omitted from the type ordering for
  17. // this package.
  18. Filter(*Context, *types.Type) bool
  19. // Header should return a header for the file, including comment markers.
  20. // Useful for copyright notices and doc strings. Include an
  21. // autogeneration notice! Do not include the "package x" line.
  22. Header(filename string) []byte
  23. // Generators returns the list of generators for this package. It is
  24. // allowed for more than one generator to write to the same file.
  25. // A Context is passed in case the list of generators depends on the
  26. // input types.
  27. Generators(*Context) []Generator
  28. }
  29. // File is
  30. type File struct {
  31. Name string
  32. FileType string
  33. PackageName string
  34. Header []byte
  35. Imports map[string]struct{}
  36. Vars bytes.Buffer
  37. Consts bytes.Buffer
  38. Body bytes.Buffer
  39. }
  40. // FileType is
  41. type FileType interface {
  42. AssembleFile(f *File, path string) error
  43. VerifyFile(f *File, path string) error
  44. }
  45. // Packages is a list of packages to generate.
  46. type Packages []Package
  47. // Generator is the contract for anything that wants to do auto-generation.
  48. // It's expected that the io.Writers passed to the below functions will be
  49. // ErrorTrackers; this allows implementations to not check for io errors,
  50. // making more readable code.
  51. //
  52. // The call order for the functions that take a Context is:
  53. // 1. Filter() // Subsequent calls see only types that pass this.
  54. // 2. Namers() // Subsequent calls see the namers provided by this.
  55. // 3. PackageVars()
  56. // 4. PackageConsts()
  57. // 5. Init()
  58. // 6. GenerateType() // Called N times, once per type in the context's Order.
  59. // 7. Imports()
  60. //
  61. // You may have multiple generators for the same file.
  62. type Generator interface {
  63. // The name of this generator. Will be included in generated comments.
  64. Name() string
  65. // Filter should return true if this generator cares about this type.
  66. // (otherwise, GenerateType will not be called.)
  67. //
  68. // Filter is called before any of the generator's other functions;
  69. // subsequent calls will get a context with only the types that passed
  70. // this filter.
  71. Filter(*Context, *types.Type) bool
  72. // If this generator needs special namers, return them here. These will
  73. // override the original namers in the context if there is a collision.
  74. // You may return nil if you don't need special names. These names will
  75. // be available in the context passed to the rest of the generator's
  76. // functions.
  77. //
  78. // A use case for this is to return a namer that tracks imports.
  79. Namers(*Context) namer.NameSystems
  80. // Init should write an init function, and any other content that's not
  81. // generated per-type. (It's not intended for generator specific
  82. // initialization! Do that when your Package constructs the
  83. // Generators.)
  84. Init(*Context, io.Writer) error
  85. // Finalize should write finish up functions, and any other content that's not
  86. // generated per-type.
  87. Finalize(*Context, io.Writer) error
  88. // PackageVars should emit an array of variable lines. They will be
  89. // placed in a var ( ... ) block. There's no need to include a leading
  90. // \t or trailing \n.
  91. PackageVars(*Context) []string
  92. // PackageConsts should emit an array of constant lines. They will be
  93. // placed in a const ( ... ) block. There's no need to include a leading
  94. // \t or trailing \n.
  95. PackageConsts(*Context) []string
  96. // GenerateType should emit the code for a particular type.
  97. GenerateType(*Context, *types.Type, io.Writer) error
  98. // Imports should return a list of necessary imports. They will be
  99. // formatted correctly. You do not need to include quotation marks,
  100. // return only the package name; alternatively, you can also return
  101. // imports in the format `name "path/to/pkg"`. Imports will be called
  102. // after Init, PackageVars, PackageConsts, and GenerateType, to allow
  103. // you to keep track of what imports you actually need.
  104. Imports(*Context) []string
  105. // Preferred file name of this generator, not including a path. It is
  106. // allowed for multiple generators to use the same filename, but it's
  107. // up to you to make sure they don't have colliding import names.
  108. // TODO: provide per-file import tracking, removing the requirement
  109. // that generators coordinate..
  110. Filename() string
  111. // A registered file type in the context to generate this file with. If
  112. // the FileType is not found in the context, execution will stop.
  113. FileType() string
  114. }
  115. // Context is global context for individual generators to consume.
  116. type Context struct {
  117. // A map from the naming system to the names for that system. E.g., you
  118. // might have public names and several private naming systems.
  119. Namers namer.NameSystems
  120. // All the types, in case you want to look up something.
  121. Universe types.Universe
  122. // All the user-specified packages. This is after recursive expansion.
  123. Inputs []string
  124. // The canonical ordering of the types (will be filtered by both the
  125. // Package's and Generator's Filter methods).
  126. Order []*types.Type
  127. // A set of types this context can process. If this is empty or nil,
  128. // the default "golang" filetype will be provided.
  129. FileTypes map[string]FileType
  130. // If true, Execute* calls will just verify that the existing output is
  131. // correct. (You may set this after calling NewContext.)
  132. Verify bool
  133. // Allows generators to add packages at runtime.
  134. builder *parser.Builder
  135. }
  136. // NewContext generates a context from the given builder, naming systems, and
  137. // the naming system you wish to construct the canonical ordering from.
  138. func NewContext(b *parser.Builder, nameSystems namer.NameSystems, canonicalOrderName string) (*Context, error) {
  139. universe, err := b.FindTypes()
  140. if err != nil {
  141. return nil, err
  142. }
  143. c := &Context{
  144. Namers: namer.NameSystems{},
  145. Universe: universe,
  146. Inputs: b.FindPackages(),
  147. FileTypes: map[string]FileType{
  148. GolangFileType: NewGolangFile(),
  149. },
  150. builder: b,
  151. }
  152. for name, systemNamer := range nameSystems {
  153. c.Namers[name] = systemNamer
  154. if name == canonicalOrderName {
  155. orderer := namer.Orderer{Namer: systemNamer}
  156. c.Order = orderer.OrderUniverse(universe)
  157. }
  158. }
  159. return c, nil
  160. }
  161. // AddDir adds a Go package to the context. The specified path must be a single
  162. // go package import path. GOPATH, GOROOT, and the location of your go binary
  163. // (`which go`) will all be searched, in the normal Go fashion.
  164. // Deprecated. Please use AddDirectory.
  165. func (ctxt *Context) AddDir(path string) error {
  166. return ctxt.builder.AddDirTo(path, &ctxt.Universe)
  167. }
  168. // AddDirectory adds a Go package to the context. The specified path must be a
  169. // single go package import path. GOPATH, GOROOT, and the location of your go
  170. // binary (`which go`) will all be searched, in the normal Go fashion.
  171. func (ctxt *Context) AddDirectory(path string) (*types.Package, error) {
  172. return ctxt.builder.AddDirectoryTo(path, &ctxt.Universe)
  173. }