forked from RecoLabs/gnata
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdeep_equal.go
More file actions
90 lines (85 loc) · 1.89 KB
/
deep_equal.go
File metadata and controls
90 lines (85 loc) · 1.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package gnata
import (
"encoding/json"
"github.com/rbbydotdev/gnata-sqlite/internal/evaluator"
)
// deepEqualInternal is the external-facing deep equality that normalizes the
// JSONata null sentinel to Go nil. This lets callers compare evaluator output
// against json.Unmarshal'd expectations (where JSON null becomes Go nil).
// The internal evaluator.DeepEqual remains strict (null ≠ undefined).
func deepEqualInternal(a, b any) bool {
a = normalizeNull(a)
b = normalizeNull(b)
return deepEqNorm(a, b)
}
func normalizeNull(v any) any {
if evaluator.IsNull(v) {
return nil
}
if n, ok := v.(json.Number); ok {
f, err := n.Float64()
if err == nil {
return f
}
}
return v
}
func deepEqNorm(a, b any) bool {
a = normalizeNull(a)
b = normalizeNull(b)
if a == nil && b == nil {
return true
}
if a == nil || b == nil {
return false
}
switch av := a.(type) {
case bool:
bv, ok := b.(bool)
return ok && av == bv
case float64:
bv, ok := b.(float64)
return ok && av == bv
case string:
bv, ok := b.(string)
return ok && av == bv
case []any:
bv, ok := b.([]any)
if !ok || len(av) != len(bv) {
return false
}
for i := range av {
if !deepEqNorm(av[i], bv[i]) {
return false
}
}
return true
case map[string]any:
if !evaluator.IsMap(b) || evaluator.MapLen(b) != len(av) {
return false
}
for k, va := range av {
vb, exists := evaluator.MapGet(b, k)
if !exists || !deepEqNorm(normalizeNull(va), normalizeNull(vb)) {
return false
}
}
return true
case *evaluator.OrderedMap:
if !evaluator.IsMap(b) || evaluator.MapLen(b) != av.Len() {
return false
}
equal := true
av.Range(func(k string, va any) bool {
vb, exists := evaluator.MapGet(b, k)
if !exists || !deepEqNorm(normalizeNull(va), normalizeNull(vb)) {
equal = false
return false
}
return true
})
return equal
default:
return evaluator.DeepEqual(a, b)
}
}