297 lines
6.5 KiB
Go
Executable file
297 lines
6.5 KiB
Go
Executable file
package form
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"sort"
|
|
|
|
diff "github.com/abcum/surreal/util/diff"
|
|
)
|
|
|
|
func NewAsciiFormatter(left map[string]interface{}) *AsciiFormatter {
|
|
return &AsciiFormatter{
|
|
left: left,
|
|
ShowArrayIndex: false,
|
|
}
|
|
}
|
|
|
|
type AsciiFormatter struct {
|
|
left map[string]interface{}
|
|
ShowArrayIndex bool
|
|
buffer string
|
|
path []string
|
|
size []int
|
|
inArray []bool
|
|
}
|
|
|
|
func (f *AsciiFormatter) Format(diff diff.Diff) (result string, err error) {
|
|
f.buffer = ""
|
|
f.path = []string{}
|
|
f.size = []int{}
|
|
f.inArray = []bool{}
|
|
|
|
f.printIndent(AsciiSame)
|
|
f.println("{")
|
|
f.push("ROOT", len(f.left), false)
|
|
f.processObject(f.left, diff.Deltas())
|
|
f.pop()
|
|
f.printIndent(AsciiSame)
|
|
f.println("}")
|
|
|
|
return f.buffer, nil
|
|
}
|
|
|
|
func (f *AsciiFormatter) processArray(array []interface{}, deltas []diff.Delta) error {
|
|
patchedIndex := 0
|
|
for index, value := range array {
|
|
f.processItem(value, deltas, diff.Index(index))
|
|
patchedIndex++
|
|
}
|
|
|
|
// additional Added
|
|
for _, delta := range deltas {
|
|
switch delta.(type) {
|
|
case *diff.Added:
|
|
d := delta.(*diff.Added)
|
|
// skip items already processed
|
|
if int(d.Position.(diff.Index)) < len(array) {
|
|
continue
|
|
}
|
|
f.printRecursive(d.Position.String(), d.Value, AsciiAdded)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (f *AsciiFormatter) processObject(object map[string]interface{}, deltas []diff.Delta) error {
|
|
names := sortedKeys(object)
|
|
for _, name := range names {
|
|
value := object[name]
|
|
f.processItem(value, deltas, diff.Name(name))
|
|
}
|
|
|
|
// Added
|
|
for _, delta := range deltas {
|
|
switch delta.(type) {
|
|
case *diff.Added:
|
|
d := delta.(*diff.Added)
|
|
f.printRecursive(d.Position.String(), d.Value, AsciiAdded)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (f *AsciiFormatter) processItem(value interface{}, deltas []diff.Delta, position diff.Position) error {
|
|
matchedDeltas := f.searchDeltas(deltas, position)
|
|
positionStr := position.String()
|
|
if len(matchedDeltas) > 0 {
|
|
for _, matchedDelta := range matchedDeltas {
|
|
|
|
switch matchedDelta.(type) {
|
|
case *diff.Object:
|
|
d := matchedDelta.(*diff.Object)
|
|
switch value.(type) {
|
|
case map[string]interface{}:
|
|
//ok
|
|
default:
|
|
return errors.New("Type mismatch")
|
|
}
|
|
o := value.(map[string]interface{})
|
|
|
|
f.printKeyWithIndent(positionStr, AsciiSame)
|
|
f.println("{")
|
|
f.push(positionStr, len(o), false)
|
|
f.processObject(o, d.Deltas)
|
|
f.pop()
|
|
f.printIndent(AsciiSame)
|
|
f.print("}")
|
|
f.printComma()
|
|
|
|
case *diff.Array:
|
|
d := matchedDelta.(*diff.Array)
|
|
switch value.(type) {
|
|
case []interface{}:
|
|
//ok
|
|
default:
|
|
return errors.New("Type mismatch")
|
|
}
|
|
a := value.([]interface{})
|
|
|
|
f.printKeyWithIndent(positionStr, AsciiSame)
|
|
f.println("[")
|
|
f.push(positionStr, len(a), true)
|
|
f.processArray(a, d.Deltas)
|
|
f.pop()
|
|
f.printIndent(AsciiSame)
|
|
f.print("]")
|
|
f.printComma()
|
|
|
|
case *diff.Added:
|
|
d := matchedDelta.(*diff.Added)
|
|
f.printRecursive(positionStr, d.Value, AsciiAdded)
|
|
f.size[len(f.size)-1]++
|
|
|
|
case *diff.Modified:
|
|
d := matchedDelta.(*diff.Modified)
|
|
savedSize := f.size[len(f.size)-1]
|
|
f.printRecursive(positionStr, d.OldValue, AsciiDeleted)
|
|
f.size[len(f.size)-1] = savedSize
|
|
f.printRecursive(positionStr, d.NewValue, AsciiAdded)
|
|
|
|
case *diff.TextDiff:
|
|
savedSize := f.size[len(f.size)-1]
|
|
d := matchedDelta.(*diff.TextDiff)
|
|
f.printRecursive(positionStr, d.OldValue, AsciiDeleted)
|
|
f.size[len(f.size)-1] = savedSize
|
|
f.printRecursive(positionStr, d.NewValue, AsciiAdded)
|
|
|
|
case *diff.Deleted:
|
|
d := matchedDelta.(*diff.Deleted)
|
|
f.printRecursive(positionStr, d.Value, AsciiDeleted)
|
|
|
|
default:
|
|
return errors.New("Unknown Delta type detected")
|
|
}
|
|
|
|
}
|
|
} else {
|
|
f.printRecursive(positionStr, value, AsciiSame)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (f *AsciiFormatter) searchDeltas(deltas []diff.Delta, postion diff.Position) (results []diff.Delta) {
|
|
results = make([]diff.Delta, 0)
|
|
for _, delta := range deltas {
|
|
switch delta.(type) {
|
|
case diff.PostDelta:
|
|
if delta.(diff.PostDelta).PostPosition() == postion {
|
|
results = append(results, delta)
|
|
}
|
|
case diff.PreDelta:
|
|
if delta.(diff.PreDelta).PrePosition() == postion {
|
|
results = append(results, delta)
|
|
}
|
|
default:
|
|
panic("heh")
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
const (
|
|
AsciiSame = " "
|
|
AsciiAdded = "+"
|
|
AsciiDeleted = "-"
|
|
)
|
|
|
|
func (f *AsciiFormatter) push(name string, size int, array bool) {
|
|
f.path = append(f.path, name)
|
|
f.size = append(f.size, size)
|
|
f.inArray = append(f.inArray, array)
|
|
}
|
|
|
|
func (f *AsciiFormatter) pop() {
|
|
f.path = f.path[0 : len(f.path)-1]
|
|
f.size = f.size[0 : len(f.size)-1]
|
|
f.inArray = f.inArray[0 : len(f.inArray)-1]
|
|
}
|
|
|
|
func (f *AsciiFormatter) printIndent(marker string) {
|
|
f.print(marker)
|
|
for n := 0; n < len(f.path); n++ {
|
|
f.print(" ")
|
|
}
|
|
}
|
|
|
|
func (f *AsciiFormatter) printKeyWithIndent(name string, marker string) {
|
|
f.printIndent(marker)
|
|
if !f.inArray[len(f.inArray)-1] {
|
|
f.printf(`"%s": `, name)
|
|
} else if f.ShowArrayIndex {
|
|
f.printf(`%s: `, name)
|
|
}
|
|
}
|
|
|
|
func (f *AsciiFormatter) printComma() {
|
|
f.size[len(f.size)-1]--
|
|
if f.size[len(f.size)-1] > 0 {
|
|
f.println(",")
|
|
} else {
|
|
f.println()
|
|
}
|
|
}
|
|
|
|
func (f *AsciiFormatter) printValue(value interface{}) {
|
|
switch value.(type) {
|
|
case string:
|
|
f.buffer += fmt.Sprintf(`"%s"`, value)
|
|
default:
|
|
f.buffer += fmt.Sprintf(`%#v`, value)
|
|
}
|
|
}
|
|
|
|
func (f *AsciiFormatter) print(a ...interface{}) {
|
|
f.buffer += fmt.Sprint(a...)
|
|
}
|
|
|
|
func (f *AsciiFormatter) printf(format string, a ...interface{}) {
|
|
f.buffer += fmt.Sprintf(format, a...)
|
|
}
|
|
|
|
func (f *AsciiFormatter) println(a ...interface{}) {
|
|
f.buffer += fmt.Sprintln(a...)
|
|
}
|
|
|
|
func (f *AsciiFormatter) printRecursive(name string, value interface{}, marker string) {
|
|
switch value.(type) {
|
|
case map[string]interface{}:
|
|
f.printKeyWithIndent(name, marker)
|
|
f.println("{")
|
|
|
|
m := value.(map[string]interface{})
|
|
size := len(m)
|
|
f.push(name, size, false)
|
|
|
|
keys := sortedKeys(m)
|
|
for _, key := range keys {
|
|
f.printRecursive(key, m[key], marker)
|
|
}
|
|
f.pop()
|
|
|
|
f.printIndent(marker)
|
|
f.print("}")
|
|
f.printComma()
|
|
case []interface{}:
|
|
f.printKeyWithIndent(name, marker)
|
|
f.println("[")
|
|
|
|
s := value.([]interface{})
|
|
size := len(s)
|
|
f.push("", size, true)
|
|
for _, item := range s {
|
|
f.printRecursive("", item, marker)
|
|
}
|
|
f.pop()
|
|
|
|
f.printIndent(marker)
|
|
f.print("]")
|
|
f.printComma()
|
|
default:
|
|
f.printKeyWithIndent(name, marker)
|
|
f.printValue(value)
|
|
f.printComma()
|
|
}
|
|
}
|
|
|
|
func sortedKeys(m map[string]interface{}) (keys []string) {
|
|
keys = make([]string, 0, len(m))
|
|
for key, _ := range m {
|
|
keys = append(keys, key)
|
|
}
|
|
sort.Strings(keys)
|
|
return
|
|
}
|