131 lines
2.9 KiB
Go
131 lines
2.9 KiB
Go
package diff
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
dmp "github.com/sergi/go-diff/diffmatchpatch"
|
|
"io"
|
|
"strconv"
|
|
)
|
|
|
|
type Unmarshaller struct {
|
|
}
|
|
|
|
func NewUnmarshaller() *Unmarshaller {
|
|
return &Unmarshaller{}
|
|
}
|
|
|
|
func (um *Unmarshaller) UnmarshalBytes(diffBytes []byte) (Diff, error) {
|
|
var diffObj map[string]interface{}
|
|
json.Unmarshal(diffBytes, &diffObj)
|
|
return um.UnmarshalObject(diffObj)
|
|
}
|
|
|
|
func (um *Unmarshaller) UnmarshalString(diffString string) (Diff, error) {
|
|
return um.UnmarshalBytes([]byte(diffString))
|
|
}
|
|
|
|
func (um *Unmarshaller) UnmarshalReader(diffReader io.Reader) (Diff, error) {
|
|
var diffBytes []byte
|
|
io.ReadFull(diffReader, diffBytes)
|
|
return um.UnmarshalBytes(diffBytes)
|
|
}
|
|
|
|
func (um *Unmarshaller) UnmarshalObject(diffObj map[string]interface{}) (Diff, error) {
|
|
result, err := process(Name(""), diffObj)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &diff{deltas: result.(*Object).Deltas}, nil
|
|
}
|
|
|
|
func process(position Position, object interface{}) (Delta, error) {
|
|
var delta Delta
|
|
switch object.(type) {
|
|
case map[string]interface{}:
|
|
o := object.(map[string]interface{})
|
|
if isArray, typed := o["_t"]; typed && isArray == "a" {
|
|
deltas := make([]Delta, 0, len(o))
|
|
for name, value := range o {
|
|
if name == "_t" {
|
|
continue
|
|
}
|
|
|
|
normalizedName := name
|
|
if normalizedName[0] == '_' {
|
|
normalizedName = name[1:]
|
|
}
|
|
index, err := strconv.Atoi(normalizedName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
childDelta, err := process(Index(index), value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
deltas = append(deltas, childDelta)
|
|
}
|
|
|
|
for _, d := range deltas {
|
|
switch d.(type) {
|
|
case *Moved:
|
|
moved := d.(*Moved)
|
|
|
|
var dd interface{}
|
|
var i int
|
|
for i, dd = range deltas {
|
|
switch dd.(type) {
|
|
case *Moved:
|
|
case PostDelta:
|
|
pd := dd.(PostDelta)
|
|
if moved.PostPosition() == pd.PostPosition() {
|
|
moved.Delta = pd
|
|
deltas = append(deltas[:i], deltas[i+1:]...)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
delta = NewArray(position, deltas)
|
|
} else {
|
|
deltas := make([]Delta, 0, len(o))
|
|
for name, value := range o {
|
|
childDelta, err := process(Name(name), value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
deltas = append(deltas, childDelta)
|
|
}
|
|
delta = NewObject(position, deltas)
|
|
}
|
|
case []interface{}:
|
|
o := object.([]interface{})
|
|
switch len(o) {
|
|
case 1:
|
|
delta = NewAdded(position, o[0])
|
|
case 2:
|
|
delta = NewModified(position, o[0], o[1])
|
|
case 3:
|
|
switch o[2] {
|
|
case float64(0):
|
|
delta = NewDeleted(position, o[0])
|
|
case float64(2):
|
|
dmp := dmp.New()
|
|
patches, err := dmp.PatchFromText(o[0].(string))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
delta = NewTextDiff(position, patches, nil, nil)
|
|
case float64(3):
|
|
delta = NewMoved(position, Index(int(o[1].(float64))), nil, nil)
|
|
default:
|
|
return nil, errors.New("Unknown delta type")
|
|
}
|
|
}
|
|
}
|
|
|
|
return delta, nil
|
|
}
|