ascii.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. package formatter
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. "sort"
  7. diff "github.com/yudai/gojsondiff"
  8. )
  9. func NewAsciiFormatter(left interface{}, config AsciiFormatterConfig) *AsciiFormatter {
  10. return &AsciiFormatter{
  11. left: left,
  12. config: config,
  13. }
  14. }
  15. type AsciiFormatter struct {
  16. left interface{}
  17. config AsciiFormatterConfig
  18. buffer *bytes.Buffer
  19. path []string
  20. size []int
  21. inArray []bool
  22. line *AsciiLine
  23. }
  24. type AsciiFormatterConfig struct {
  25. ShowArrayIndex bool
  26. Coloring bool
  27. }
  28. var AsciiFormatterDefaultConfig = AsciiFormatterConfig{}
  29. type AsciiLine struct {
  30. marker string
  31. indent int
  32. buffer *bytes.Buffer
  33. }
  34. func (f *AsciiFormatter) Format(diff diff.Diff) (result string, err error) {
  35. f.buffer = bytes.NewBuffer([]byte{})
  36. f.path = []string{}
  37. f.size = []int{}
  38. f.inArray = []bool{}
  39. if v, ok := f.left.(map[string]interface{}); ok {
  40. f.formatObject(v, diff)
  41. } else if v, ok := f.left.([]interface{}); ok {
  42. f.formatArray(v, diff)
  43. } else {
  44. return "", fmt.Errorf("expected map[string]interface{} or []interface{}, got %T",
  45. f.left)
  46. }
  47. return f.buffer.String(), nil
  48. }
  49. func (f *AsciiFormatter) formatObject(left map[string]interface{}, df diff.Diff) {
  50. f.addLineWith(AsciiSame, "{")
  51. f.push("ROOT", len(left), false)
  52. f.processObject(left, df.Deltas())
  53. f.pop()
  54. f.addLineWith(AsciiSame, "}")
  55. }
  56. func (f *AsciiFormatter) formatArray(left []interface{}, df diff.Diff) {
  57. f.addLineWith(AsciiSame, "[")
  58. f.push("ROOT", len(left), true)
  59. f.processArray(left, df.Deltas())
  60. f.pop()
  61. f.addLineWith(AsciiSame, "]")
  62. }
  63. func (f *AsciiFormatter) processArray(array []interface{}, deltas []diff.Delta) error {
  64. patchedIndex := 0
  65. for index, value := range array {
  66. f.processItem(value, deltas, diff.Index(index))
  67. patchedIndex++
  68. }
  69. // additional Added
  70. for _, delta := range deltas {
  71. switch delta.(type) {
  72. case *diff.Added:
  73. d := delta.(*diff.Added)
  74. // skip items already processed
  75. if int(d.Position.(diff.Index)) < len(array) {
  76. continue
  77. }
  78. f.printRecursive(d.Position.String(), d.Value, AsciiAdded)
  79. }
  80. }
  81. return nil
  82. }
  83. func (f *AsciiFormatter) processObject(object map[string]interface{}, deltas []diff.Delta) error {
  84. names := sortedKeys(object)
  85. for _, name := range names {
  86. value := object[name]
  87. f.processItem(value, deltas, diff.Name(name))
  88. }
  89. // Added
  90. for _, delta := range deltas {
  91. switch delta.(type) {
  92. case *diff.Added:
  93. d := delta.(*diff.Added)
  94. f.printRecursive(d.Position.String(), d.Value, AsciiAdded)
  95. }
  96. }
  97. return nil
  98. }
  99. func (f *AsciiFormatter) processItem(value interface{}, deltas []diff.Delta, position diff.Position) error {
  100. matchedDeltas := f.searchDeltas(deltas, position)
  101. positionStr := position.String()
  102. if len(matchedDeltas) > 0 {
  103. for _, matchedDelta := range matchedDeltas {
  104. switch matchedDelta.(type) {
  105. case *diff.Object:
  106. d := matchedDelta.(*diff.Object)
  107. switch value.(type) {
  108. case map[string]interface{}:
  109. //ok
  110. default:
  111. return errors.New("Type mismatch")
  112. }
  113. o := value.(map[string]interface{})
  114. f.newLine(AsciiSame)
  115. f.printKey(positionStr)
  116. f.print("{")
  117. f.closeLine()
  118. f.push(positionStr, len(o), false)
  119. f.processObject(o, d.Deltas)
  120. f.pop()
  121. f.newLine(AsciiSame)
  122. f.print("}")
  123. f.printComma()
  124. f.closeLine()
  125. case *diff.Array:
  126. d := matchedDelta.(*diff.Array)
  127. switch value.(type) {
  128. case []interface{}:
  129. //ok
  130. default:
  131. return errors.New("Type mismatch")
  132. }
  133. a := value.([]interface{})
  134. f.newLine(AsciiSame)
  135. f.printKey(positionStr)
  136. f.print("[")
  137. f.closeLine()
  138. f.push(positionStr, len(a), true)
  139. f.processArray(a, d.Deltas)
  140. f.pop()
  141. f.newLine(AsciiSame)
  142. f.print("]")
  143. f.printComma()
  144. f.closeLine()
  145. case *diff.Added:
  146. d := matchedDelta.(*diff.Added)
  147. f.printRecursive(positionStr, d.Value, AsciiAdded)
  148. f.size[len(f.size)-1]++
  149. case *diff.Modified:
  150. d := matchedDelta.(*diff.Modified)
  151. savedSize := f.size[len(f.size)-1]
  152. f.printRecursive(positionStr, d.OldValue, AsciiDeleted)
  153. f.size[len(f.size)-1] = savedSize
  154. f.printRecursive(positionStr, d.NewValue, AsciiAdded)
  155. case *diff.TextDiff:
  156. savedSize := f.size[len(f.size)-1]
  157. d := matchedDelta.(*diff.TextDiff)
  158. f.printRecursive(positionStr, d.OldValue, AsciiDeleted)
  159. f.size[len(f.size)-1] = savedSize
  160. f.printRecursive(positionStr, d.NewValue, AsciiAdded)
  161. case *diff.Deleted:
  162. d := matchedDelta.(*diff.Deleted)
  163. f.printRecursive(positionStr, d.Value, AsciiDeleted)
  164. default:
  165. return errors.New("Unknown Delta type detected")
  166. }
  167. }
  168. } else {
  169. f.printRecursive(positionStr, value, AsciiSame)
  170. }
  171. return nil
  172. }
  173. func (f *AsciiFormatter) searchDeltas(deltas []diff.Delta, postion diff.Position) (results []diff.Delta) {
  174. results = make([]diff.Delta, 0)
  175. for _, delta := range deltas {
  176. switch delta.(type) {
  177. case diff.PostDelta:
  178. if delta.(diff.PostDelta).PostPosition() == postion {
  179. results = append(results, delta)
  180. }
  181. case diff.PreDelta:
  182. if delta.(diff.PreDelta).PrePosition() == postion {
  183. results = append(results, delta)
  184. }
  185. default:
  186. panic("heh")
  187. }
  188. }
  189. return
  190. }
  191. const (
  192. AsciiSame = " "
  193. AsciiAdded = "+"
  194. AsciiDeleted = "-"
  195. )
  196. var AsciiStyles = map[string]string{
  197. AsciiAdded: "30;42",
  198. AsciiDeleted: "30;41",
  199. }
  200. func (f *AsciiFormatter) push(name string, size int, array bool) {
  201. f.path = append(f.path, name)
  202. f.size = append(f.size, size)
  203. f.inArray = append(f.inArray, array)
  204. }
  205. func (f *AsciiFormatter) pop() {
  206. f.path = f.path[0 : len(f.path)-1]
  207. f.size = f.size[0 : len(f.size)-1]
  208. f.inArray = f.inArray[0 : len(f.inArray)-1]
  209. }
  210. func (f *AsciiFormatter) addLineWith(marker string, value string) {
  211. f.line = &AsciiLine{
  212. marker: marker,
  213. indent: len(f.path),
  214. buffer: bytes.NewBufferString(value),
  215. }
  216. f.closeLine()
  217. }
  218. func (f *AsciiFormatter) newLine(marker string) {
  219. f.line = &AsciiLine{
  220. marker: marker,
  221. indent: len(f.path),
  222. buffer: bytes.NewBuffer([]byte{}),
  223. }
  224. }
  225. func (f *AsciiFormatter) closeLine() {
  226. style, ok := AsciiStyles[f.line.marker]
  227. if f.config.Coloring && ok {
  228. f.buffer.WriteString("\x1b[" + style + "m")
  229. }
  230. f.buffer.WriteString(f.line.marker)
  231. for n := 0; n < f.line.indent; n++ {
  232. f.buffer.WriteString(" ")
  233. }
  234. f.buffer.Write(f.line.buffer.Bytes())
  235. if f.config.Coloring && ok {
  236. f.buffer.WriteString("\x1b[0m")
  237. }
  238. f.buffer.WriteRune('\n')
  239. }
  240. func (f *AsciiFormatter) printKey(name string) {
  241. if !f.inArray[len(f.inArray)-1] {
  242. fmt.Fprintf(f.line.buffer, `"%s": `, name)
  243. } else if f.config.ShowArrayIndex {
  244. fmt.Fprintf(f.line.buffer, `%s: `, name)
  245. }
  246. }
  247. func (f *AsciiFormatter) printComma() {
  248. f.size[len(f.size)-1]--
  249. if f.size[len(f.size)-1] > 0 {
  250. f.line.buffer.WriteRune(',')
  251. }
  252. }
  253. func (f *AsciiFormatter) printValue(value interface{}) {
  254. switch value.(type) {
  255. case string:
  256. fmt.Fprintf(f.line.buffer, `"%s"`, value)
  257. case nil:
  258. f.line.buffer.WriteString("null")
  259. default:
  260. fmt.Fprintf(f.line.buffer, `%#v`, value)
  261. }
  262. }
  263. func (f *AsciiFormatter) print(a string) {
  264. f.line.buffer.WriteString(a)
  265. }
  266. func (f *AsciiFormatter) printRecursive(name string, value interface{}, marker string) {
  267. switch value.(type) {
  268. case map[string]interface{}:
  269. f.newLine(marker)
  270. f.printKey(name)
  271. f.print("{")
  272. f.closeLine()
  273. m := value.(map[string]interface{})
  274. size := len(m)
  275. f.push(name, size, false)
  276. keys := sortedKeys(m)
  277. for _, key := range keys {
  278. f.printRecursive(key, m[key], marker)
  279. }
  280. f.pop()
  281. f.newLine(marker)
  282. f.print("}")
  283. f.printComma()
  284. f.closeLine()
  285. case []interface{}:
  286. f.newLine(marker)
  287. f.printKey(name)
  288. f.print("[")
  289. f.closeLine()
  290. s := value.([]interface{})
  291. size := len(s)
  292. f.push("", size, true)
  293. for _, item := range s {
  294. f.printRecursive("", item, marker)
  295. }
  296. f.pop()
  297. f.newLine(marker)
  298. f.print("]")
  299. f.printComma()
  300. f.closeLine()
  301. default:
  302. f.newLine(marker)
  303. f.printKey(name)
  304. f.printValue(value)
  305. f.printComma()
  306. f.closeLine()
  307. }
  308. }
  309. func sortedKeys(m map[string]interface{}) (keys []string) {
  310. keys = make([]string, 0, len(m))
  311. for key, _ := range m {
  312. keys = append(keys, key)
  313. }
  314. sort.Strings(keys)
  315. return
  316. }