-
Notifications
You must be signed in to change notification settings - Fork 4
/
gpath.go
129 lines (114 loc) · 2.94 KB
/
gpath.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
package gpath
import (
"errors"
"go/ast"
"go/constant"
"go/parser"
"reflect"
)
// At access a field of v by a path.
// v must be struct or pointer of struct.
// A path is represented by Go's expression which can be parsed by go/parser.ParseExpr.
// You can use selectors and indexes in a path.
// Slice and arrays index allow only expressions of int.
// Maps key allow only expressions of string, int and float64.
func At(v interface{}, path string) (interface{}, error) {
path = "v." + path
expr, err := parser.ParseExpr(path)
if err != nil {
return nil, err
}
ev, err := at(reflect.ValueOf(v), expr)
if err != nil {
return nil, err
}
if ev == (reflect.Value{}) {
return nil, nil
}
return ev.Interface(), nil
}
func at(v reflect.Value, expr ast.Expr) (reflect.Value, error) {
switch v.Kind() {
case reflect.Ptr, reflect.Interface:
return at(v.Elem(), expr)
}
switch expr := expr.(type) {
case *ast.Ident:
return v, nil
case *ast.SelectorExpr:
return atBySelector(v, expr)
case *ast.IndexExpr:
return atByIndex(v, expr)
default:
return reflect.Value{}, errors.New("does not support expr")
}
}
func direct(v reflect.Value) reflect.Value {
switch v.Kind() {
case reflect.Ptr, reflect.Interface:
return v.Elem()
default:
return v
}
}
func atBySelector(v reflect.Value, expr *ast.SelectorExpr) (reflect.Value, error) {
ev, err := at(v, expr.X)
if err != nil {
return reflect.Value{}, err
}
ev = direct(ev)
switch ev.Kind() {
case reflect.Struct:
fv := ev.FieldByName(expr.Sel.Name)
if fv == (reflect.Value{}) {
return reflect.Value{}, errors.New("cannot find field")
}
return fv, nil
default:
return reflect.Value{}, errors.New("does not support selector type")
}
}
func atByIndex(v reflect.Value, expr *ast.IndexExpr) (reflect.Value, error) {
ev, err := at(v, expr.X)
if err != nil {
return reflect.Value{}, err
}
ev = direct(ev)
idx, err := evalExpr(expr.Index)
if err != nil {
return reflect.Value{}, err
}
switch ev.Kind() {
case reflect.Slice, reflect.Array:
i, ok := constant.Int64Val(idx)
if !ok {
return reflect.Value{}, errors.New("does not support index type")
}
return ev.Index(int(i)), nil
case reflect.Map:
switch idx.Kind() {
case constant.Int:
k, ok := constant.Int64Val(idx)
if !ok {
return reflect.Value{}, errors.New("does not support index type")
}
return ev.MapIndex(reflect.ValueOf(int(k))), nil
case constant.Float:
k, ok := constant.Float64Val(idx)
if !ok {
return reflect.Value{}, errors.New("does not support index type")
}
return ev.MapIndex(reflect.ValueOf(k)), nil
case constant.String:
k := constant.StringVal(idx)
return ev.MapIndex(reflect.ValueOf(k)), nil
case constant.Bool:
k := constant.BoolVal(idx)
return ev.MapIndex(reflect.ValueOf(k)), nil
default:
return reflect.Value{}, errors.New("does not support index type")
}
default:
return reflect.Value{}, errors.New("does not support expr type")
}
}