123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- package generator
- import (
- "fmt"
- "io"
- "runtime"
- "text/template"
- )
- // SnippetWriter is an attempt to make the template library usable.
- // Methods are chainable, and you don't have to check Error() until you're all
- // done.
- type SnippetWriter struct {
- w io.Writer
- context *Context
- // Left & right delimiters. text/template defaults to "{{" and "}}"
- // which is totally unusable for go code based templates.
- left, right string
- funcMap template.FuncMap
- err error
- }
- // NewSnippetWriter is
- // w is the destination; left and right are the delimiters; @ and $ are both
- // reasonable choices.
- //
- // c is used to make a function for every naming system, to which you can pass
- // a type and get the corresponding name.
- func NewSnippetWriter(w io.Writer, c *Context, left, right string) *SnippetWriter {
- sw := &SnippetWriter{
- w: w,
- context: c,
- left: left,
- right: right,
- funcMap: template.FuncMap{},
- }
- for name, namer := range c.Namers {
- sw.funcMap[name] = namer.Name
- }
- return sw
- }
- // Do parses format and runs args through it. You can have arbitrary logic in
- // the format (see the text/template documentation), but consider running many
- // short templaces, with ordinary go logic in between--this may be more
- // readable. Do is chainable. Any error causes every other call to do to be
- // ignored, and the error will be returned by Error(). So you can check it just
- // once, at the end of your function.
- //
- // 'args' can be quite literally anything; read the text/template documentation
- // for details. Maps and structs work particularly nicely. Conveniently, the
- // types package is designed to have structs that are easily referencable from
- // the template language.
- //
- // Example:
- //
- // sw := generator.NewSnippetWriter(outBuffer, context, "$", "$")
- // sw.Do(`The public type name is: $.type|public$`, map[string]interface{}{"type": t})
- // return sw.Error()
- //
- // Where:
- // * "$" starts a template directive
- // * "." references the entire thing passed as args
- // * "type" therefore sees a map and looks up the key "type"
- // * "|" means "pass the thing on the left to the thing on the right"
- // * "public" is the name of a naming system, so the SnippetWriter has given
- // the template a function called "public" that takes a *types.Type and
- // returns the naming system's name. E.g., if the type is "string" this might
- // return "String".
- // * the second "$" ends the template directive.
- //
- // The map is actually not necessary. The below does the same thing:
- //
- // sw.Do(`The public type name is: $.|public$`, t)
- //
- // You may or may not find it more readable to use the map with a descriptive
- // key, but if you want to pass more than one arg, the map or a custom struct
- // becomes a requirement. You can do arbitrary logic inside these templates,
- // but you should consider doing the logic in go and stitching them together
- // for the sake of your readers.
- //
- // TODO: Change Do() to optionally take a list of pairs of parameters (key, value)
- // and have it construct a combined map with that and args.
- func (s *SnippetWriter) Do(format string, args interface{}) *SnippetWriter {
- if s.err != nil {
- return s
- }
- // Name the template by source file:line so it can be found when
- // there's an error.
- _, file, line, _ := runtime.Caller(1)
- tmpl, err := template.
- New(fmt.Sprintf("%s:%d", file, line)).
- Delims(s.left, s.right).
- Funcs(s.funcMap).
- Parse(format)
- if err != nil {
- s.err = err
- return s
- }
- err = tmpl.Execute(s.w, args)
- if err != nil {
- s.err = err
- }
- return s
- }
- // Args exists to make it convenient to construct arguments for
- // SnippetWriter.Do.
- type Args map[interface{}]interface{}
- // With makes a copy of a and adds the given key, value pair.
- func (a Args) With(key, value interface{}) Args {
- a2 := Args{key: value}
- for k, v := range a {
- a2[k] = v
- }
- return a2
- }
- // WithArgs makes a copy of a and adds the given arguments.
- func (a Args) WithArgs(rhs Args) Args {
- a2 := Args{}
- for k, v := range rhs {
- a2[k] = v
- }
- for k, v := range a {
- a2[k] = v
- }
- return a2
- }
- // Out is
- func (s *SnippetWriter) Out() io.Writer {
- return s.w
- }
- // Error returns any encountered error.
- func (s *SnippetWriter) Error() error {
- return s.err
- }
|