package generator import ( "bytes" "io" "go-common/app/tool/gengo/namer" "go-common/app/tool/gengo/parser" "go-common/app/tool/gengo/types" ) // Package contains the contract for generating a package. type Package interface { // Name returns the package short name. Name() string // Path returns the package import path. Path() string // Filter should return true if this package cares about this type. // Otherwise, this type will be omitted from the type ordering for // this package. Filter(*Context, *types.Type) bool // Header should return a header for the file, including comment markers. // Useful for copyright notices and doc strings. Include an // autogeneration notice! Do not include the "package x" line. Header(filename string) []byte // Generators returns the list of generators for this package. It is // allowed for more than one generator to write to the same file. // A Context is passed in case the list of generators depends on the // input types. Generators(*Context) []Generator } // File is type File struct { Name string FileType string PackageName string Header []byte Imports map[string]struct{} Vars bytes.Buffer Consts bytes.Buffer Body bytes.Buffer } // FileType is type FileType interface { AssembleFile(f *File, path string) error VerifyFile(f *File, path string) error } // Packages is a list of packages to generate. type Packages []Package // Generator is the contract for anything that wants to do auto-generation. // It's expected that the io.Writers passed to the below functions will be // ErrorTrackers; this allows implementations to not check for io errors, // making more readable code. // // The call order for the functions that take a Context is: // 1. Filter() // Subsequent calls see only types that pass this. // 2. Namers() // Subsequent calls see the namers provided by this. // 3. PackageVars() // 4. PackageConsts() // 5. Init() // 6. GenerateType() // Called N times, once per type in the context's Order. // 7. Imports() // // You may have multiple generators for the same file. type Generator interface { // The name of this generator. Will be included in generated comments. Name() string // Filter should return true if this generator cares about this type. // (otherwise, GenerateType will not be called.) // // Filter is called before any of the generator's other functions; // subsequent calls will get a context with only the types that passed // this filter. Filter(*Context, *types.Type) bool // If this generator needs special namers, return them here. These will // override the original namers in the context if there is a collision. // You may return nil if you don't need special names. These names will // be available in the context passed to the rest of the generator's // functions. // // A use case for this is to return a namer that tracks imports. Namers(*Context) namer.NameSystems // Init should write an init function, and any other content that's not // generated per-type. (It's not intended for generator specific // initialization! Do that when your Package constructs the // Generators.) Init(*Context, io.Writer) error // Finalize should write finish up functions, and any other content that's not // generated per-type. Finalize(*Context, io.Writer) error // PackageVars should emit an array of variable lines. They will be // placed in a var ( ... ) block. There's no need to include a leading // \t or trailing \n. PackageVars(*Context) []string // PackageConsts should emit an array of constant lines. They will be // placed in a const ( ... ) block. There's no need to include a leading // \t or trailing \n. PackageConsts(*Context) []string // GenerateType should emit the code for a particular type. GenerateType(*Context, *types.Type, io.Writer) error // Imports should return a list of necessary imports. They will be // formatted correctly. You do not need to include quotation marks, // return only the package name; alternatively, you can also return // imports in the format `name "path/to/pkg"`. Imports will be called // after Init, PackageVars, PackageConsts, and GenerateType, to allow // you to keep track of what imports you actually need. Imports(*Context) []string // Preferred file name of this generator, not including a path. It is // allowed for multiple generators to use the same filename, but it's // up to you to make sure they don't have colliding import names. // TODO: provide per-file import tracking, removing the requirement // that generators coordinate.. Filename() string // A registered file type in the context to generate this file with. If // the FileType is not found in the context, execution will stop. FileType() string } // Context is global context for individual generators to consume. type Context struct { // A map from the naming system to the names for that system. E.g., you // might have public names and several private naming systems. Namers namer.NameSystems // All the types, in case you want to look up something. Universe types.Universe // All the user-specified packages. This is after recursive expansion. Inputs []string // The canonical ordering of the types (will be filtered by both the // Package's and Generator's Filter methods). Order []*types.Type // A set of types this context can process. If this is empty or nil, // the default "golang" filetype will be provided. FileTypes map[string]FileType // If true, Execute* calls will just verify that the existing output is // correct. (You may set this after calling NewContext.) Verify bool // Allows generators to add packages at runtime. builder *parser.Builder } // NewContext generates a context from the given builder, naming systems, and // the naming system you wish to construct the canonical ordering from. func NewContext(b *parser.Builder, nameSystems namer.NameSystems, canonicalOrderName string) (*Context, error) { universe, err := b.FindTypes() if err != nil { return nil, err } c := &Context{ Namers: namer.NameSystems{}, Universe: universe, Inputs: b.FindPackages(), FileTypes: map[string]FileType{ GolangFileType: NewGolangFile(), }, builder: b, } for name, systemNamer := range nameSystems { c.Namers[name] = systemNamer if name == canonicalOrderName { orderer := namer.Orderer{Namer: systemNamer} c.Order = orderer.OrderUniverse(universe) } } return c, nil } // AddDir adds a Go package to the context. The specified path must be a single // go package import path. GOPATH, GOROOT, and the location of your go binary // (`which go`) will all be searched, in the normal Go fashion. // Deprecated. Please use AddDirectory. func (ctxt *Context) AddDir(path string) error { return ctxt.builder.AddDirTo(path, &ctxt.Universe) } // AddDirectory adds a Go package to the context. The specified path must be a // single go package import path. GOPATH, GOROOT, and the location of your go // binary (`which go`) will all be searched, in the normal Go fashion. func (ctxt *Context) AddDirectory(path string) (*types.Package, error) { return ctxt.builder.AddDirectoryTo(path, &ctxt.Universe) }