You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
140 lines
4.1 KiB
140 lines
4.1 KiB
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 |
|
}
|
|
|