| /* |
| Copyright 2015 The Kubernetes Authors. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| 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 |
| } |
| |
| // 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 |
| } |
| |
| func (s *SnippetWriter) Out() io.Writer { |
| return s.w |
| } |
| |
| // Error returns any encountered error. |
| func (s *SnippetWriter) Error() error { |
| return s.err |
| } |