cmd_tpl.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. // Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
  2. //
  3. // This Source Code Form is subject to the terms of the MIT License.
  4. // If a copy of the MIT was not distributed with this file,
  5. // You can obtain one at https://github.com/gogf/gf.
  6. package cmd
  7. import (
  8. "context"
  9. "github.com/gogf/gf/v2/encoding/gjson"
  10. "github.com/gogf/gf/v2/errors/gerror"
  11. "github.com/gogf/gf/v2/frame/g"
  12. "github.com/gogf/gf/v2/os/gfile"
  13. "github.com/gogf/gf/v2/text/gstr"
  14. "github.com/gogf/gf/v2/util/gtag"
  15. "github.com/gogf/gf/v2/util/gutil"
  16. "github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
  17. )
  18. var (
  19. Tpl = cTpl{}
  20. )
  21. type cTpl struct {
  22. g.Meta `name:"tpl" brief:"{cTplBrief}" dc:"{cTplDc}"`
  23. }
  24. const (
  25. cTplBrief = `template parsing and building commands`
  26. cTplDc = `
  27. The "tpl" command is used for template parsing and building purpose.
  28. It can parse either template file or folder with multiple types of values support,
  29. like json/xml/yaml/toml/ini.
  30. `
  31. cTplParseBrief = `parse either template file or folder with multiple types of values`
  32. cTplParseEg = `
  33. gf tpl parse -p ./template -v values.json -r
  34. gf tpl parse -p ./template -v values.json -n *.tpl -r
  35. gf tpl parse -p ./template -v values.json -d '${{,}}' -r
  36. gf tpl parse -p ./template -v values.json -o ./template.parsed
  37. `
  38. cTplSupportValuesFilePattern = `*.json,*.xml,*.yaml,*.yml,*.toml,*.ini`
  39. )
  40. type (
  41. cTplParseInput struct {
  42. g.Meta `name:"parse" brief:"{cTplParseBrief}" eg:"{cTplParseEg}"`
  43. Path string `name:"path" short:"p" brief:"template file or folder path" v:"required"`
  44. Pattern string `name:"pattern" short:"n" brief:"template file pattern when path is a folder, default is:*" d:"*"`
  45. Recursive bool `name:"recursive" short:"c" brief:"recursively parsing files if path is folder, default is:true" d:"true"`
  46. Values string `name:"values" short:"v" brief:"template values file/folder, support file types like: json/xml/yaml/toml/ini" v:"required"`
  47. Output string `name:"output" short:"o" brief:"output file/folder path"`
  48. Delimiters string `name:"delimiters" short:"d" brief:"delimiters for template content parsing, default is:{{,}}" d:"{{,}}"`
  49. Replace bool `name:"replace" short:"r" brief:"replace original files" orphan:"true"`
  50. }
  51. cTplParseOutput struct{}
  52. )
  53. func init() {
  54. gtag.Sets(g.MapStrStr{
  55. `cTplBrief`: cTplBrief,
  56. `cTplDc`: cTplDc,
  57. `cTplParseEg`: cTplParseEg,
  58. `cTplParseBrief`: cTplParseBrief,
  59. })
  60. }
  61. func (c *cTpl) Parse(ctx context.Context, in cTplParseInput) (out *cTplParseOutput, err error) {
  62. if in.Output == "" && !in.Replace {
  63. return nil, gerror.New(`parameter output and replace should not be both empty`)
  64. }
  65. delimiters := gstr.SplitAndTrim(in.Delimiters, ",")
  66. mlog.Debugf("delimiters input:%s, parsed:%#v", in.Delimiters, delimiters)
  67. if len(delimiters) != 2 {
  68. return nil, gerror.Newf(`invalid delimiters: %s`, in.Delimiters)
  69. }
  70. g.View().SetDelimiters(delimiters[0], delimiters[1])
  71. valuesMap, err := c.loadValues(ctx, in.Values)
  72. if err != nil {
  73. return nil, err
  74. }
  75. if len(valuesMap) == 0 {
  76. return nil, gerror.Newf(`empty values loaded from values file/folder "%s"`, in.Values)
  77. }
  78. err = c.parsePath(ctx, valuesMap, in)
  79. if err == nil {
  80. mlog.Print("done!")
  81. }
  82. return
  83. }
  84. func (c *cTpl) parsePath(ctx context.Context, values g.Map, in cTplParseInput) (err error) {
  85. if !gfile.Exists(in.Path) {
  86. return gerror.Newf(`path "%s" does not exist`, in.Path)
  87. }
  88. var (
  89. path string
  90. files []string
  91. relativePath string
  92. outputPath string
  93. )
  94. path = gfile.RealPath(in.Path)
  95. if gfile.IsDir(path) {
  96. files, err = gfile.ScanDirFile(path, in.Pattern, in.Recursive)
  97. if err != nil {
  98. return err
  99. }
  100. for _, file := range files {
  101. relativePath = gstr.Replace(file, path, "")
  102. if in.Output != "" {
  103. outputPath = gfile.Join(in.Output, relativePath)
  104. }
  105. if err = c.parseFile(ctx, file, outputPath, values, in); err != nil {
  106. return
  107. }
  108. }
  109. return
  110. }
  111. if in.Output != "" {
  112. outputPath = in.Output
  113. }
  114. err = c.parseFile(ctx, path, outputPath, values, in)
  115. return
  116. }
  117. func (c *cTpl) parseFile(ctx context.Context, file string, output string, values g.Map, in cTplParseInput) (err error) {
  118. output = gstr.ReplaceByMap(output, g.MapStrStr{
  119. `\\`: `\`,
  120. `//`: `/`,
  121. })
  122. content, err := g.View().Parse(ctx, file, values)
  123. if err != nil {
  124. return err
  125. }
  126. if output != "" {
  127. mlog.Printf(`parse file "%s" to "%s"`, file, output)
  128. return gfile.PutContents(output, content)
  129. }
  130. if in.Replace {
  131. mlog.Printf(`parse and replace file "%s"`, file)
  132. return gfile.PutContents(file, content)
  133. }
  134. return nil
  135. }
  136. func (c *cTpl) loadValues(ctx context.Context, valuesPath string) (data g.Map, err error) {
  137. if !gfile.Exists(valuesPath) {
  138. return nil, gerror.Newf(`values file/folder "%s" does not exist`, valuesPath)
  139. }
  140. var j *gjson.Json
  141. if gfile.IsDir(valuesPath) {
  142. var valueFiles []string
  143. valueFiles, err = gfile.ScanDirFile(valuesPath, cTplSupportValuesFilePattern, true)
  144. if err != nil {
  145. return nil, err
  146. }
  147. data = make(g.Map)
  148. for _, file := range valueFiles {
  149. if j, err = gjson.Load(file); err != nil {
  150. return nil, err
  151. }
  152. gutil.MapMerge(data, j.Map())
  153. }
  154. return
  155. }
  156. if j, err = gjson.Load(valuesPath); err != nil {
  157. return nil, err
  158. }
  159. data = j.Map()
  160. return
  161. }