Skip to content

Commit

Permalink
feat: add merge errors for incompatible values (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
jensneuse authored Jan 2, 2025
1 parent 15ca0ac commit 534f686
Show file tree
Hide file tree
Showing 5 changed files with 426 additions and 243 deletions.
123 changes: 123 additions & 0 deletions mergevalues.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package astjson

import (
"bytes"
"errors"
)

var (
ErrMergeDifferentTypes = errors.New("cannot merge different types")
ErrMergeDifferingArrayLengths = errors.New("cannot merge arrays of differing lengths")
ErrMergeUnknownType = errors.New("cannot merge unknown type")
)

func MergeValues(a, b *Value) (v *Value, changed bool, err error) {
if a == nil {
return b, true, nil
}
if b == nil {
return a, false, nil
}
aBool, bBool := a.Type() == TypeTrue || a.Type() == TypeFalse, b.Type() == TypeTrue || b.Type() == TypeFalse
booleans := aBool && bBool
oneIsNull := a.Type() == TypeNull || b.Type() == TypeNull
if a.Type() != b.Type() && !booleans && !oneIsNull {
return nil, false, ErrMergeDifferentTypes
}
if b.Type() == TypeNull && a.Type() != TypeNull {
return b, true, nil
}
switch a.Type() {
case TypeObject:
ao, _ := a.Object()
bo, _ := b.Object()
ao.unescapeKeys()
bo.unescapeKeys()
for i := range bo.kvs {
k := bo.kvs[i].k
r := bo.kvs[i].v
l := ao.Get(k)
if l == nil {
ao.Set(k, r)
continue
}
n, changed, err := MergeValues(l, r)
if err != nil {
return nil, false, err
}
if changed {
ao.Set(k, n)
}
}
return a, false, nil
case TypeArray:
aa, _ := a.Array()
ba, _ := b.Array()
if len(aa) == 0 {
return b, true, nil
}
if len(ba) == 0 {
return a, false, nil
}
if len(aa) != len(ba) {
return nil, false, ErrMergeDifferingArrayLengths
}
for i := range aa {
n, changed, err := MergeValues(aa[i], ba[i])
if err != nil {
return nil, false, err
}
if changed {
aa[i] = n
}
}
return a, false, nil
case TypeFalse:
if b.Type() == TypeTrue {
return b, true, nil
}
return a, false, nil
case TypeTrue:
if b.Type() == TypeFalse {
return b, true, nil
}
return a, false, nil
case TypeNull:
if b.Type() != TypeNull {
return b, true, nil
}
return a, false, nil
case TypeNumber:
af, _ := a.Float64()
bf, _ := b.Float64()
if af != bf {
return b, true, nil
}
return a, false, nil
case TypeString:
as, _ := a.StringBytes()
bs, _ := b.StringBytes()
if !bytes.Equal(as, bs) {
return b, true, nil
}
return a, false, nil
default:
return nil, false, ErrMergeUnknownType
}
}

func MergeValuesWithPath(a, b *Value, path ...string) (v *Value, changed bool, err error) {
if len(path) == 0 {
return MergeValues(a, b)
}
root := &Value{
t: TypeObject,
}
current := root
for i := 0; i < len(path)-1; i++ {
current.Set(path[i], &Value{t: TypeObject})
current = current.Get(path[i])
}
current.Set(path[len(path)-1], b)
return MergeValues(a, root)
}
Loading

0 comments on commit 534f686

Please sign in to comment.