This repository has been archived by the owner on Mar 8, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 27
/
external.go
148 lines (142 loc) · 3.17 KB
/
external.go
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package nodes
import (
"fmt"
)
// External is a node interface that can be implemented by other packages.
type External interface {
// Kind returns a node kind.
Kind() Kind
// Value returns a primitive value of the node or nil if node is not a value.
Value() Value
// SameAs check if the node is exactly the same node as n2. This usually means checking node pointers.
SameAs(n2 External) bool
}
// ExternalArray is an analog of Array type.
type ExternalArray interface {
External
// Size returns the number of child nodes.
Size() int
// ValueAt returns a array value by an index.
ValueAt(i int) External
}
// ExternalObject is an analog of Object type.
type ExternalObject interface {
External
// Size returns the number of fields in an object.
Size() int
// Keys returns a sorted list of keys (object field names).
Keys() []string
// ValueAt returns an object field by key. It returns false if key does not exist.
ValueAt(key string) (External, bool)
}
// toNodeExt converts the external node to a native node type.
// The returned value is the copy of an original node.
func toNodeExt(n External) (Node, error) {
if n == nil {
return nil, nil
}
switch kind := n.Kind(); kind {
case KindNil:
return nil, nil
case KindObject:
o, ok := n.(ExternalObject)
if !ok {
return nil, fmt.Errorf("node type %T returns a %v kind, but doesn't implement the interface", n, kind)
}
keys := o.Keys()
m := make(Object, len(keys))
for _, k := range keys {
nv, ok := o.ValueAt(k)
if !ok {
return nil, fmt.Errorf("node type %T: key %q is listed, but cannot be fetched", n, k)
}
v, err := toNodeExt(nv)
if err != nil {
return nil, err
}
m[k] = v
}
return m, nil
case KindArray:
a, ok := n.(ExternalArray)
if !ok {
return nil, fmt.Errorf("node type %T returns a %v kind, but doesn't implement the interface", n, kind)
}
sz := a.Size()
m := make(Array, sz)
for i := 0; i < sz; i++ {
nv := a.ValueAt(i)
v, err := toNodeExt(nv)
if err != nil {
return nil, err
}
m[i] = v
}
return m, nil
default:
return n.Value(), nil
}
}
// equalExt compares two external nodes.
func equalExt(n1, n2 External) bool {
k1, k2 := n1.Kind(), n2.Kind()
switch k1 {
case KindObject:
if k2 != KindObject {
return false
}
o1, ok := n1.(ExternalObject)
if !ok {
return false
}
o2, ok := n2.(ExternalObject)
if !ok {
return false
}
if o1.Size() != o2.Size() {
return false
}
keys1, keys2 := o1.Keys(), o2.Keys()
m := make(map[string]struct{}, len(keys1))
for _, k := range keys1 {
m[k] = struct{}{}
}
for _, k := range keys2 {
if _, ok := m[k]; !ok {
return false
}
v1, _ := o1.ValueAt(k)
v2, _ := o2.ValueAt(k)
if !Equal(v1, v2) {
return false
}
}
return true
case KindArray:
if k2 != KindArray {
return false
}
a1, ok := n1.(ExternalArray)
if !ok {
return false
}
a2, ok := n2.(ExternalArray)
if !ok {
return false
}
sz := a1.Size()
if sz != a2.Size() {
return false
}
for i := 0; i < sz; i++ {
v1 := a1.ValueAt(i)
v2 := a2.ValueAt(i)
if !Equal(v1, v2) {
return false
}
}
return true
default:
return Equal(n1.Value(), n2.Value())
}
}