-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpath.go
More file actions
123 lines (108 loc) · 2.8 KB
/
Copy pathpath.go
File metadata and controls
123 lines (108 loc) · 2.8 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
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
package jpath
import "slices"
type (
// Path is a compiled query function chain. Calling it executes the query
// against a JSON document
Path func(document any) []any
// SegmentFunc processes input nodes and returns output nodes for one
// segment step
SegmentFunc func(in []any, root any) []any
// SelectorFunc appends selected output nodes for one input node
SelectorFunc func(out []any, node, root any) []any
)
// ComposePath composes segment functions into an executable Path
func ComposePath(segments ...SegmentFunc) Path {
chain := composeSegments(segments)
return func(document any) []any {
return chain([]any{document}, document)
}
}
// ChildSegment builds a non-descendant segment from selector functions
func ChildSegment(selectors ...SelectorFunc) SegmentFunc {
return composeSegment(selectors, false)
}
// DescendantSegment builds a descendant segment from selector functions
func DescendantSegment(selectors ...SelectorFunc) SegmentFunc {
return composeSegment(selectors, true)
}
func composeSegments(segments []SegmentFunc) SegmentFunc {
if len(segments) == 1 {
return segments[0]
}
segments = slices.Clone(segments)
return func(in []any, root any) []any {
for _, seg := range segments {
in = seg(in, root)
}
return in
}
}
func composeSegment(selectors []SelectorFunc, descendant bool) SegmentFunc {
chain := composeSelectors(selectors)
if descendant {
return func(in []any, root any) []any {
desc := descendantsOf(in)
out := make([]any, 0)
for _, node := range desc {
out = chain(out, node, root)
}
return out
}
}
return func(in []any, root any) []any {
out := make([]any, 0)
for _, node := range in {
out = chain(out, node, root)
}
return out
}
}
func makeDescendantSegment(selectors []SelectorFunc) SegmentFunc {
chain := composeSelectors(selectors)
return func(in []any, root any) []any {
out := make([]any, 0)
for _, node := range in {
walkDescendants(node, func(node any) {
out = chain(out, node, root)
})
}
return out
}
}
func composeSelectors(selectors []SelectorFunc) SelectorFunc {
if len(selectors) == 1 {
return selectors[0]
}
selectors = slices.Clone(selectors)
return func(out []any, node, root any) []any {
for _, selector := range selectors {
out = selector(out, node, root)
}
return out
}
}
func selectorIdentity(out []any, _, _ any) []any {
return out
}
func descendantsOf(nodes []any) []any {
res := make([]any, 0, len(nodes))
for _, node := range nodes {
walkDescendants(node, func(v any) {
res = append(res, v)
})
}
return res
}
func walkDescendants(node any, visit func(any)) {
visit(node)
switch v := node.(type) {
case []any:
for _, elem := range v {
walkDescendants(elem, visit)
}
case map[string]any:
for _, key := range sortedKeys(v) {
walkDescendants(v[key], visit)
}
}
}