PageRenderTime 57ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/internal/cmd/units-codegen/template.go

http://github.com/smyrman/units
Go | 215 lines | 192 code | 19 blank | 4 comment | 29 complexity | eb92f6b7f9f7c398fb62de57ddb874b9 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. package main
  2. import (
  3. "fmt"
  4. "regexp"
  5. "strings"
  6. "sync"
  7. "text/template"
  8. )
  9. type pkgConfig struct {
  10. sync.RWMutex
  11. compiled bool
  12. PkgName string
  13. Types map[string]typeDef
  14. }
  15. // Compile realizes generator fields so that the struct can be used for template rendering.
  16. func (p *pkgConfig) Compile() error {
  17. if p.compiled {
  18. return nil
  19. }
  20. wgs := make(map[string]*sync.WaitGroup)
  21. for k := range p.Types {
  22. wgs[k] = &sync.WaitGroup{}
  23. wgs[k].Add(1)
  24. }
  25. p.RLock()
  26. for k, t := range p.Types {
  27. go t.compileRoutine(k, p, wgs)
  28. }
  29. p.RUnlock()
  30. for k, wg := range wgs {
  31. wg.Wait()
  32. p.RLock()
  33. err := p.Types[k].compileError
  34. p.RUnlock()
  35. if err != nil {
  36. return err
  37. }
  38. }
  39. p.compiled = true
  40. return nil
  41. }
  42. type typeDef struct {
  43. compileError error
  44. PkgName string
  45. Import []string
  46. Self string
  47. Name string `json:"-"`
  48. DivDurationType string
  49. MulDurationType string
  50. Units []unitDef
  51. UnitGenerators []unitGenerator
  52. }
  53. // compileRoutine should be started concurrently with go, and all typeDefs should be passed the same WaitGroup lookup.
  54. func (t typeDef) compileRoutine(name string, p *pkgConfig, wgs map[string]*sync.WaitGroup) {
  55. defer wgs[name].Done()
  56. defer func() {
  57. p.Lock()
  58. p.Types[name] = t
  59. p.Unlock()
  60. }()
  61. var hasTime bool
  62. t.Name = name
  63. t.PkgName = p.PkgName
  64. // Append import time if needed.
  65. if len(t.DivDurationType) > 0 || len(t.MulDurationType) > 0 {
  66. hasTime = false
  67. for _, s := range t.Import {
  68. if s == "time" {
  69. hasTime = true
  70. break
  71. }
  72. }
  73. if !hasTime {
  74. t.Import = append(t.Import, "time")
  75. }
  76. }
  77. // Generate units -- wait for dependent types to complete.
  78. for i, g := range t.UnitGenerators {
  79. wg, ok := wgs[g.From]
  80. if !ok {
  81. t.compileError = fmt.Errorf("types.%s.unitGenerators[%d].from: '%s' not found in type lookup",
  82. name, i, g.From,
  83. )
  84. return
  85. }
  86. wg.Wait()
  87. p.RLock()
  88. units, err := g.units(name, p.Types)
  89. p.RUnlock()
  90. if err != nil {
  91. t.compileError = fmt.Errorf("types.%s.unitGenerators[%d]%s", name, i, err.Error())
  92. return
  93. }
  94. t.Units = append(t.Units, units...)
  95. }
  96. if len(t.UnitGenerators) > 0 {
  97. t.UnitGenerators = nil
  98. }
  99. }
  100. type unitDef struct {
  101. Name string
  102. NamePlural string
  103. Value string
  104. }
  105. type unitGenerator struct {
  106. From string
  107. Pattern string
  108. NameSuffix string
  109. ValueSuffix string
  110. }
  111. func (g unitGenerator) units(name string, types map[string]typeDef) ([]unitDef, error) {
  112. var r *regexp.Regexp
  113. var err error
  114. t, ok := types[g.From]
  115. if !ok {
  116. return nil, fmt.Errorf(".from: '%s' not found in type lookup", g.From)
  117. }
  118. if g.Pattern != "" {
  119. r, err = regexp.Compile(g.Pattern)
  120. if err != nil {
  121. return nil, fmt.Errorf(".pattern: %s", err.Error())
  122. }
  123. }
  124. units := make([]unitDef, 0, len(t.Units))
  125. for _, from := range t.Units {
  126. if r != nil && !r.MatchString(from.Name) {
  127. continue
  128. }
  129. units = append(units, unitDef{
  130. Name: from.Name + g.NameSuffix,
  131. NamePlural: from.NamePlural + g.NameSuffix,
  132. Value: fmt.Sprintf("%s(%s)%s", name, from.Name, g.ValueSuffix),
  133. })
  134. }
  135. return units, nil
  136. }
  137. var tmplFunctions = template.FuncMap{
  138. "lower": strings.ToLower,
  139. }
  140. const tmpl = `// This code is generated by units-codegen; do not manualy edit this file.
  141. {{$self := .Self}}{{$name := .Name}}
  142. package {{.PkgName}}
  143. {{if .Import}}import ({{range .Import}}
  144. "{{.}}"{{end}}
  145. ){{end}}
  146. // Units for {{.Name}} values. Always multiply with a unit when setting the initial value like you would for
  147. // time.Time. This prevents you from having to worry about the internal storage format.
  148. const ({{range .Units}}
  149. {{.Name}} {{$name}} = {{.Value}}{{end}}
  150. )
  151. {{range .Units}}
  152. // {{.NamePlural}} returns {{$self}} as a floating point number of {{.NamePlural|lower}}.
  153. func ({{$self}} {{$name}}) {{.NamePlural}}() float64 {
  154. return float64({{$self}} / {{.Name}})
  155. }
  156. {{end}}
  157. // Abs returns the absolute value of {{.Self}} as a copy.
  158. func ({{.Self}} {{.Name}}) Abs() {{.Name}} {
  159. if {{.Self}} < 0 {
  160. return -{{.Self}}
  161. }
  162. return {{.Self}}
  163. }
  164. // Mul returns the product of {{.Self}} * x as a new {{.Name}}.
  165. func ({{.Self}} {{.Name}}) Mul(x float64) {{.Name}} {
  166. return {{.Self}} * {{.Name}}(x)
  167. }
  168. // Div returns the quotient of {{.Self}} / x as a new {{.Name}}.
  169. func ({{.Self}} {{.Name}}) Div(x float64) {{.Name}} {
  170. return {{.Self}} / {{.Name}}(x)
  171. }
  172. // Div{{.Name}} returns the quotient of {{.Self}} / x as a floating point number.
  173. func ({{.Self}} {{.Name}}) Div{{.Name}}(x {{.Name}}) float64 {
  174. return float64({{.Self}} / x)
  175. }
  176. {{if .DivDurationType}}
  177. // DivDuration returns the quotient of {{.Self}} / t as a {{.DivDurationType}}.
  178. func ({{.Self}} {{.Name}}) DivDuration(t time.Duration) {{.DivDurationType}} {
  179. return {{.DivDurationType}}(float64({{.Self}}) / float64(t))
  180. }
  181. // Div{{.DivDurationType}} returns the quotient of {{.Self}} / x as a time.Duration.
  182. func ({{.Self}} {{.Name}}) Div{{.DivDurationType}}(x {{.DivDurationType}}) time.Duration {
  183. return time.Duration(float64({{.Self}}) / float64(x))
  184. }{{end}}
  185. {{if .MulDurationType}}
  186. // MulDuration returns the product of {{.Self}} * t as a {{.MulDurationType}}.
  187. func ({{.Self}} {{.Name}}) MulDuration(t time.Duration) {{.MulDurationType}} {
  188. return {{.MulDurationType}}(float64({{.Self}}) * float64(t))
  189. }{{end}}
  190. `