141 lines
4.1 KiB
Go
141 lines
4.1 KiB
Go
|
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
|
||
|
}
|