Skip to content

Commit a15438b

Browse files
author
dmitriy kalinin
committed
add FindOp
1 parent 661a67c commit a15438b

File tree

7 files changed

+526
-0
lines changed

7 files changed

+526
-0
lines changed

patch/find_op.go

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package patch
2+
3+
import (
4+
"fmt"
5+
)
6+
7+
type FindOp struct {
8+
Path Pointer
9+
}
10+
11+
func (op FindOp) Apply(doc interface{}) (interface{}, error) {
12+
tokens := op.Path.Tokens()
13+
14+
if len(tokens) == 1 {
15+
return doc, nil
16+
}
17+
18+
obj := doc
19+
20+
for i, token := range tokens[1:] {
21+
isLast := i == len(tokens)-2
22+
23+
switch typedToken := token.(type) {
24+
case IndexToken:
25+
idx := typedToken.Index
26+
27+
typedObj, ok := obj.([]interface{})
28+
if !ok {
29+
return nil, newOpArrayMismatchTypeErr(tokens[:i+2], obj)
30+
}
31+
32+
if idx >= len(typedObj) {
33+
errMsg := "Expected to find array index '%d' but found array of length '%d'"
34+
return nil, fmt.Errorf(errMsg, idx, len(typedObj))
35+
}
36+
37+
if isLast {
38+
return typedObj[idx], nil
39+
} else {
40+
obj = typedObj[idx]
41+
}
42+
43+
case AfterLastIndexToken:
44+
errMsg := "Expected not to find after last index token in path '%s' (not supported in find operations)"
45+
return nil, fmt.Errorf(errMsg, op.Path)
46+
47+
case MatchingIndexToken:
48+
typedObj, ok := obj.([]interface{})
49+
if !ok {
50+
return nil, newOpArrayMismatchTypeErr(tokens[:i+2], obj)
51+
}
52+
53+
var idxs []int
54+
55+
for itemIdx, item := range typedObj {
56+
typedItem, ok := item.(map[interface{}]interface{})
57+
if ok {
58+
if typedItem[typedToken.Key] == typedToken.Value {
59+
idxs = append(idxs, itemIdx)
60+
}
61+
}
62+
}
63+
64+
if typedToken.Optional && len(idxs) == 0 {
65+
obj = map[interface{}]interface{}{typedToken.Key: typedToken.Value}
66+
67+
if isLast {
68+
return obj, nil
69+
}
70+
} else {
71+
if len(idxs) != 1 {
72+
errMsg := "Expected to find exactly one matching array item for path '%s' but found %d"
73+
return nil, fmt.Errorf(errMsg, NewPointer(tokens[:i+2]), len(idxs))
74+
}
75+
76+
idx := idxs[0]
77+
78+
if isLast {
79+
return typedObj[idx], nil
80+
} else {
81+
obj = typedObj[idx]
82+
}
83+
}
84+
85+
case KeyToken:
86+
typedObj, ok := obj.(map[interface{}]interface{})
87+
if !ok {
88+
return nil, newOpMapMismatchTypeErr(tokens[:i+2], obj)
89+
}
90+
91+
var found bool
92+
93+
obj, found = typedObj[typedToken.Key]
94+
if !found && !typedToken.Optional {
95+
errMsg := "Expected to find a map key '%s' for path '%s'"
96+
return nil, fmt.Errorf(errMsg, typedToken.Key, NewPointer(tokens[:i+2]))
97+
}
98+
99+
if isLast {
100+
return typedObj[typedToken.Key], nil
101+
} else {
102+
if !found {
103+
// Determine what type of value to create based on next token
104+
switch tokens[i+2].(type) {
105+
case MatchingIndexToken:
106+
obj = []interface{}{}
107+
case KeyToken:
108+
obj = map[interface{}]interface{}{}
109+
default:
110+
errMsg := "Expected to find key or matching index token at path '%s'"
111+
return nil, fmt.Errorf(errMsg, NewPointer(tokens[:i+3]))
112+
}
113+
}
114+
}
115+
116+
default:
117+
return nil, fmt.Errorf("Expected to not find token '%T' at '%s'", token, NewPointer(tokens[:i+2]))
118+
}
119+
}
120+
121+
return doc, nil
122+
}

0 commit comments

Comments
 (0)