snippet_writer.go 4.1 KB

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