json_visitor.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. package raymond
  2. import (
  3. "encoding/json"
  4. "github.com/mailgun/raymond/v2/ast"
  5. "strings"
  6. )
  7. type list []interface{}
  8. func (l *list) Add(item interface{}) {
  9. *l = append(*l, item)
  10. }
  11. func (l *list) Get() interface{} {
  12. return (*l)[0]
  13. }
  14. func (l *list) Len() int {
  15. return len(*l)
  16. }
  17. func newList(item interface{}) *list {
  18. l := new(list)
  19. l.Add(item)
  20. return l
  21. }
  22. type JSONVisitor struct {
  23. JSON map[string]interface{}
  24. ctx *handlebarsContext
  25. }
  26. func newJSONVisitor() *JSONVisitor {
  27. j := map[string]interface{}{}
  28. v := &JSONVisitor{JSON: j, ctx: newHandlebarsContext()}
  29. return v
  30. }
  31. func ToJSON(node ast.Node) string {
  32. visitor := newJSONVisitor()
  33. node.Accept(visitor)
  34. b, _ := json.Marshal(visitor.JSON)
  35. return string(b)
  36. }
  37. func (v *JSONVisitor) VisitProgram(node *ast.Program) interface{} {
  38. for _, n := range node.Body {
  39. n.Accept(v)
  40. }
  41. return v.JSON
  42. }
  43. func (v *JSONVisitor) VisitMustache(node *ast.MustacheStatement) interface{} {
  44. node.Expression.Accept(v)
  45. return nil
  46. }
  47. func (v *JSONVisitor) VisitBlock(node *ast.BlockStatement) interface{} {
  48. var action string
  49. fp := node.Expression.FieldPath()
  50. if fp != nil {
  51. action = node.Expression.HelperName()
  52. }
  53. if action == "with" || action == "each" {
  54. blockParamsPath := make([]string, 0)
  55. blockParams := make([]string, 0)
  56. for _, params := range node.Expression.Params {
  57. // Extract block params from nested nodes.
  58. if pe, ok := params.(*ast.PathExpression); ok {
  59. blockParamsPath = append(blockParamsPath, pe.Parts...)
  60. }
  61. }
  62. if node.Program != nil {
  63. if len(node.Program.BlockParams) > 0 {
  64. blockParams = append(node.Program.BlockParams)
  65. }
  66. }
  67. if action == "each" {
  68. blockParamsPath = append(blockParamsPath, "[0]")
  69. }
  70. if len(blockParams) > 0 {
  71. v.ctx.AddMemberContext(strings.Join(blockParamsPath, "."), strings.Join(blockParams, "."))
  72. } else {
  73. v.ctx.AddMemberContext(strings.Join(blockParamsPath, "."), "")
  74. }
  75. if node.Program != nil {
  76. node.Program.Accept(v)
  77. }
  78. if node.Inverse != nil {
  79. node.Inverse.Accept(v)
  80. }
  81. v.ctx.MoveUpContext()
  82. } else {
  83. for _, param := range node.Expression.Params {
  84. param.Accept(v)
  85. }
  86. if node.Program != nil {
  87. node.Program.Accept(v)
  88. }
  89. if node.Inverse != nil {
  90. node.Inverse.Accept(v)
  91. }
  92. }
  93. return nil
  94. }
  95. func (v *JSONVisitor) VisitPartial(node *ast.PartialStatement) interface{} {
  96. return nil
  97. }
  98. func (v *JSONVisitor) VisitContent(node *ast.ContentStatement) interface{} {
  99. return nil
  100. }
  101. func (v *JSONVisitor) VisitComment(node *ast.CommentStatement) interface{} {
  102. return nil
  103. }
  104. func (v *JSONVisitor) VisitExpression(node *ast.Expression) interface{} {
  105. var action string
  106. fp := node.FieldPath()
  107. if fp != nil {
  108. if len(fp.Parts) > 0 {
  109. action = node.HelperName()
  110. if action == "lookup" {
  111. if len(node.Params) > 0 {
  112. path, ok := node.Params[0].(*ast.PathExpression)
  113. if ok {
  114. depth := path.Depth
  115. tmpPath := []string{}
  116. for _, p := range path.Parts {
  117. tmpPath = append(tmpPath, p)
  118. }
  119. for _, n := range node.Params[1:] {
  120. pe, ok := n.(*ast.PathExpression)
  121. if ok {
  122. pe.Depth = depth
  123. pe.Parts = append(tmpPath, pe.Parts...)
  124. pe.Accept(v)
  125. }
  126. }
  127. return nil
  128. }
  129. }
  130. }
  131. }
  132. }
  133. node.Path.Accept(v)
  134. for _, n := range node.Params {
  135. n.Accept(v)
  136. }
  137. return nil
  138. }
  139. func (v *JSONVisitor) VisitSubExpression(node *ast.SubExpression) interface{} {
  140. node.Expression.Accept(v)
  141. return nil
  142. }
  143. func (v *JSONVisitor) VisitPath(node *ast.PathExpression) interface{} {
  144. if node.Data {
  145. data := node.Parts[len(node.Parts)-1]
  146. if data == "index" {
  147. node.Parts[len(node.Parts)-1] = "[0]"
  148. }
  149. }
  150. if node.Scoped {
  151. if strings.HasPrefix(node.Original, ".") && !strings.HasPrefix(node.Original, "..") {
  152. if len(node.Parts) == 0 {
  153. node.Parts = []string{""}
  154. }
  155. }
  156. }
  157. res := v.ctx.GetMappedContext(node.Parts, node.Depth)
  158. v.appendToJSON(res)
  159. return nil
  160. }
  161. func (v *JSONVisitor) VisitString(node *ast.StringLiteral) interface{} {
  162. return nil
  163. }
  164. func (v *JSONVisitor) VisitBoolean(node *ast.BooleanLiteral) interface{} {
  165. return nil
  166. }
  167. func (v *JSONVisitor) VisitNumber(node *ast.NumberLiteral) interface{} {
  168. return nil
  169. }
  170. func (v *JSONVisitor) VisitHash(node *ast.Hash) interface{} {
  171. return nil
  172. }
  173. func (v *JSONVisitor) VisitHashPair(node *ast.HashPair) interface{} {
  174. return nil
  175. }
  176. func (v *JSONVisitor) appendToJSON(templateLabels []string) {
  177. var tmp interface{}
  178. tmp = v.JSON
  179. for idx, name := range templateLabels {
  180. var onArrayLabel, isArray, isLastLabel bool
  181. //Figure out if name is an array Label.
  182. if strings.HasPrefix(name, "[") {
  183. onArrayLabel = true
  184. }
  185. //Figure out if we are on a simple last label.
  186. if idx == len(templateLabels)-1 {
  187. isLastLabel = true
  188. }
  189. //Figure out if the next label is an array label.
  190. if !isLastLabel {
  191. if strings.HasPrefix(templateLabels[idx+1], "[") {
  192. isArray = true
  193. }
  194. }
  195. //Complex isLastLabel check.
  196. //Since we skip onArrayLabels not nested with another array.
  197. //foo.[0].[0] would not skip first array label.
  198. //This allows us to know it's a nested array
  199. //and not a struct value with an array.
  200. // foo.[0].baz would skip array label.
  201. // If isArray and is not isLastLabel and if
  202. // the idx is equal to the length of the slice - 2
  203. // We know this is actually the last label as we skip single instances
  204. // of an array label.
  205. if isArray && !isLastLabel {
  206. if idx == len(templateLabels)-2 {
  207. isLastLabel = true
  208. }
  209. }
  210. //If onArrayLabel and not isArray
  211. //Skip this iteration because we only care about
  212. // array labels for nested arrays.
  213. if onArrayLabel && !isArray {
  214. continue
  215. }
  216. switch c := tmp.(type) {
  217. case map[string]interface{}:
  218. if _, ok := c[name]; !ok {
  219. //If the name does not exist in the map
  220. //And is the last label.
  221. if isLastLabel {
  222. //If that last label is an array.
  223. if isArray {
  224. //Use the provided name to make a new list and mock the string value.
  225. c[name] = newList(mockStringValue(name))
  226. } else {
  227. //If it is not an array. Add the value as a mocked string value set to the current name.
  228. c[name] = mockStringValue(name)
  229. }
  230. } else {
  231. //If it is not the last label.
  232. // And the label value is an array.
  233. if isArray {
  234. //Set the label name to a new list value
  235. c[name] = new(list)
  236. } else {
  237. //If the value is not an array and it is not the last value.
  238. //It must be a map
  239. c[name] = map[string]interface{}{}
  240. }
  241. }
  242. } else {
  243. //If the name does exist in the map lets determine its type.
  244. if li, ok := c[name].(list); ok {
  245. //If it's a list and is the last value.
  246. //Set the the 0 index of the list to name
  247. //If it is not already set.
  248. if isLastLabel && li.Len() == 0 {
  249. li.Add(mockStringValue(name))
  250. }
  251. } else if _, ok := c[name].(string); ok {
  252. //If c[name]'s value is a string and it is not the last label
  253. //c[name] had been used in an if or other reference that made us
  254. // determine it was a string value. That is false turn it into a map.
  255. if !isLastLabel {
  256. c[name] = map[string]interface{}{}
  257. }
  258. }
  259. }
  260. //Update tmp to the next deepest value
  261. tmp = c[name]
  262. case *list:
  263. //If type is list.
  264. //If it is the last label and is array and on array label.
  265. //This is a special case where we know our final value is an array.
  266. //So we can just add the array and the final value.
  267. //However cause these arrays are nested at an unknown depth we use test_value
  268. //Rather than replacing it with name, because name is actually an array label.
  269. if isLastLabel && isArray && onArrayLabel {
  270. if c.Len() == 0 {
  271. c.Add(newList("test_value"))
  272. }
  273. } else if isArray && isLastLabel {
  274. //If isArray and isLastLabel.
  275. //We know that it is safe to use name for the value.
  276. //So we set it as such.
  277. if c.Len() == 0 {
  278. c.Add(mockStringValue(name))
  279. }
  280. } else if isArray {
  281. //If it is not the last item just add an array.
  282. if c.Len() == 0 {
  283. c.Add(new(list))
  284. }
  285. } else {
  286. if c.Len() == 0 {
  287. if isLastLabel {
  288. //If already in an array and no string values have been applied above.
  289. //Then this indicates a map to end this label resolution
  290. c.Add(map[string]interface{}{name: mockStringValue(name)})
  291. } else {
  292. //If not last label and not a future nested array as determined above.
  293. //Then make this a map.
  294. c.Add(map[string]interface{}{name: map[string]interface{}{}})
  295. }
  296. } else {
  297. //If c.Len is greater than zero we have already added to this array.
  298. //The only case that should fall through here is a previously created map.
  299. if _, ok := (*c)[0].(map[string]interface{}); ok {
  300. if isLastLabel {
  301. //If this is the last label assign it to the map and mock it's value.
  302. (*c)[0].(map[string]interface{})[name] = mockStringValue(name)
  303. } else {
  304. //If it's not the last label. Add just the map.
  305. (*c)[0].(map[string]interface{})[name] = (map[string]interface{}{})
  306. }
  307. }
  308. }
  309. //If we had to mess with maps assign tmp the map value matching name within the array.
  310. tmp = (*c)[0].(map[string]interface{})[name]
  311. continue
  312. }
  313. //Assign tmp to the 0 index of the array. *Note we should never have any arrays larger than a length of 1.
  314. tmp = (*c)[0]
  315. }
  316. }
  317. }
  318. func mockStringValue(name string) string {
  319. return "test_" + name
  320. }