snippet_writer.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package generator
  14. import (
  15. "fmt"
  16. "io"
  17. "runtime"
  18. "text/template"
  19. )
  20. // SnippetWriter is an attempt to make the template library usable.
  21. // Methods are chainable, and you don't have to check Error() until you're all
  22. // done.
  23. type SnippetWriter struct {
  24. w io.Writer
  25. context *Context
  26. // Left & right delimiters. text/template defaults to "{{" and "}}"
  27. // which is totally unusable for go code based templates.
  28. left, right string
  29. funcMap template.FuncMap
  30. err error
  31. }
  32. // w is the destination; left and right are the delimiters; @ and $ are both
  33. // reasonable choices.
  34. //
  35. // c is used to make a function for every naming system, to which you can pass
  36. // a type and get the corresponding name.
  37. func NewSnippetWriter(w io.Writer, c *Context, left, right string) *SnippetWriter {
  38. sw := &SnippetWriter{
  39. w: w,
  40. context: c,
  41. left: left,
  42. right: right,
  43. funcMap: template.FuncMap{},
  44. }
  45. for name, namer := range c.Namers {
  46. sw.funcMap[name] = namer.Name
  47. }
  48. return sw
  49. }
  50. // Do parses format and runs args through it. You can have arbitrary logic in
  51. // the format (see the text/template documentation), but consider running many
  52. // short templaces, with ordinary go logic in between--this may be more
  53. // readable. Do is chainable. Any error causes every other call to do to be
  54. // ignored, and the error will be returned by Error(). So you can check it just
  55. // once, at the end of your function.
  56. //
  57. // 'args' can be quite literally anything; read the text/template documentation
  58. // for details. Maps and structs work particularly nicely. Conveniently, the
  59. // types package is designed to have structs that are easily referencable from
  60. // the template language.
  61. //
  62. // Example:
  63. //
  64. // sw := generator.NewSnippetWriter(outBuffer, context, "$", "$")
  65. // sw.Do(`The public type name is: $.type|public$`, map[string]interface{}{"type": t})
  66. // return sw.Error()
  67. //
  68. // Where:
  69. // * "$" starts a template directive
  70. // * "." references the entire thing passed as args
  71. // * "type" therefore sees a map and looks up the key "type"
  72. // * "|" means "pass the thing on the left to the thing on the right"
  73. // * "public" is the name of a naming system, so the SnippetWriter has given
  74. // the template a function called "public" that takes a *types.Type and
  75. // returns the naming system's name. E.g., if the type is "string" this might
  76. // return "String".
  77. // * the second "$" ends the template directive.
  78. //
  79. // The map is actually not necessary. The below does the same thing:
  80. //
  81. // sw.Do(`The public type name is: $.|public$`, t)
  82. //
  83. // You may or may not find it more readable to use the map with a descriptive
  84. // key, but if you want to pass more than one arg, the map or a custom struct
  85. // becomes a requirement. You can do arbitrary logic inside these templates,
  86. // but you should consider doing the logic in go and stitching them together
  87. // for the sake of your readers.
  88. //
  89. // TODO: Change Do() to optionally take a list of pairs of parameters (key, value)
  90. // and have it construct a combined map with that and args.
  91. func (s *SnippetWriter) Do(format string, args interface{}) *SnippetWriter {
  92. if s.err != nil {
  93. return s
  94. }
  95. // Name the template by source file:line so it can be found when
  96. // there's an error.
  97. _, file, line, _ := runtime.Caller(1)
  98. tmpl, err := template.
  99. New(fmt.Sprintf("%s:%d", file, line)).
  100. Delims(s.left, s.right).
  101. Funcs(s.funcMap).
  102. Parse(format)
  103. if err != nil {
  104. s.err = err
  105. return s
  106. }
  107. err = tmpl.Execute(s.w, args)
  108. if err != nil {
  109. s.err = err
  110. }
  111. return s
  112. }
  113. // Args exists to make it convenient to construct arguments for
  114. // SnippetWriter.Do.
  115. type Args map[interface{}]interface{}
  116. // With makes a copy of a and adds the given key, value pair.
  117. func (a Args) With(key, value interface{}) Args {
  118. a2 := Args{key: value}
  119. for k, v := range a {
  120. a2[k] = v
  121. }
  122. return a2
  123. }
  124. // WithArgs makes a copy of a and adds the given arguments.
  125. func (a Args) WithArgs(rhs Args) Args {
  126. a2 := Args{}
  127. for k, v := range rhs {
  128. a2[k] = v
  129. }
  130. for k, v := range a {
  131. a2[k] = v
  132. }
  133. return a2
  134. }
  135. func (s *SnippetWriter) Out() io.Writer {
  136. return s.w
  137. }
  138. // Error returns any encountered error.
  139. func (s *SnippetWriter) Error() error {
  140. return s.err
  141. }