-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathselectr.go
184 lines (158 loc) · 4.14 KB
/
selectr.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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
package selectr
import (
"fmt"
"strings"
"github.com/0xch4z/selectr/internal/parser"
"github.com/0xch4z/selectr/internal/parser/ast"
)
// ResolveError represents an error that occured while resolving a
// value for a given selector.
type ResolveError struct {
Msg string
Code string
// Pos is the position in the corresponding key-path of the underlying
// expression that the resolver was dervied from.
Pos int
}
// Error implements (error).Error
func (err ResolveError) Error() string {
if err.Code != "" {
return err.Code + ": " + err.Msg
}
return err.Msg
}
// Resolver resolves a value from an object.
type Resolver interface {
Resolve(interface{}) (interface{}, error)
Expression() ast.Expr
}
// MapEntryResolver resolves a value from a map.
type MapEntryResolver struct {
Key string
Expr ast.Expr
}
// Resolve resolves the value of an entry on the map.
func (r *MapEntryResolver) Resolve(v interface{}) (interface{}, error) {
switch v := v.(type) {
case map[string]interface{}:
return v[r.Key], nil
}
return nil, ResolveError{
Code: "TypeError",
Msg: fmt.Sprintf("cannot resolve attribute '%s' on type %T", r.Key, v),
Pos: r.Expr.StartPos(),
}
}
// Expression returns the corresponding ast.Expr.
func (r *MapEntryResolver) Expression() ast.Expr {
return r.Expr
}
// MapEntryResolver implements Resolver.
var _ Resolver = (*MapEntryResolver)(nil)
// SliceElementResolve resolves values from a slice.
type SliceElementResolver struct {
Index int
Expr *ast.IndexExpr
}
// Resolve resolves the value of the element at the index on the slice.
func (r *SliceElementResolver) Resolve(v interface{}) (interface{}, error) {
switch v := v.(type) {
case []interface{}:
if r.Index > len(v)-1 {
return nil, fmt.Errorf("index out of range; index is %d but length is only %d", r.Index, len(v))
}
return v[r.Index], nil
}
return nil, ResolveError{
Code: "TypeError",
Msg: fmt.Sprintf("cannot resolve element '%d' on type %T", r.Index, v),
Pos: r.Expr.StartPos(),
}
}
// Expression returns the corresponding ast.Expr.
func (r *SliceElementResolver) Expression() ast.Expr {
return r.Expr
}
// SliceElementResolver implements Resolver.
var _ Resolver = (*SliceElementResolver)(nil)
// Parse parses a traversal tree from the selector string and returns
// a new Selector instance.
func Parse(s string) (*Selector, error) {
exprs, err := parser.New(strings.NewReader(s)).Parse()
if err != nil {
return nil, err
}
var head, tail *TraversalTreeNode
for _, expr := range exprs {
var resolver Resolver
switch e := expr.(type) {
case *ast.AttrExpr:
resolver = &MapEntryResolver{
Key: e.Attr.Lit,
Expr: e,
}
case *ast.IndexExpr:
switch indexExpr := e.Index.(type) {
case *ast.IntLit:
resolver = &SliceElementResolver{
Index: indexExpr.Value().(int),
Expr: e,
}
case *ast.StringLit:
resolver = &MapEntryResolver{
Key: indexExpr.Value().(string),
Expr: e,
}
}
}
curr := &TraversalTreeNode{
Resolver: resolver,
}
if head == nil {
head = curr
} else {
tail.Child = curr
curr.Parent = tail
}
tail = curr
}
return &Selector{
tree: head,
}, nil
}
// TraversalTreeNode represents a tree node responsible for traversing
// a given object.
type TraversalTreeNode struct {
Resolver Resolver
Parent *TraversalTreeNode
Child *TraversalTreeNode
}
// Selector represents a value selection on an object.
type Selector struct {
tree *TraversalTreeNode
}
// Resolve resolves the value at the specified key-path, if any, from the
// provided object. The root object must be an indexable type such as a
// Map `map[string]interface{}` or a Slice `[]interface{}`.
//
// All errors will be prefixed with the sub-key-path the error occured at.
//
// Example usage:
//
// sel := Parse("test[0].foo")
// sel.Resolve(map[string]interface{}{
// "test": []map[string]interface{}{
// {"foo": "bar"}
// }
// })
func (s *Selector) Resolve(v interface{}) (interface{}, error) {
curr := s.tree
for curr != nil {
var err error
if v, err = curr.Resolver.Resolve(v); err != nil {
return nil, err
}
curr = curr.Child
}
return v, nil
}