Skip to content

Commit 7efa838

Browse files
committedSep 30, 2019
feat: Update *TagExpr.Range method and fix *Validator.Validate method
Change-Id: I96d0823e593c7f2878a93b2aaa5c7edda8e6de90
1 parent 91cae92 commit 7efa838

File tree

6 files changed

+202
-110
lines changed

6 files changed

+202
-110
lines changed
 

‎fieldhandler.go

-54
This file was deleted.

‎handler.go

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package tagexpr
2+
3+
import "reflect"
4+
5+
// FieldHandler field handler
6+
type FieldHandler struct {
7+
selector string
8+
field *fieldVM
9+
expr *TagExpr
10+
}
11+
12+
func newFieldHandler(expr *TagExpr, fieldSelector string, field *fieldVM) *FieldHandler {
13+
return &FieldHandler{
14+
selector: fieldSelector,
15+
field: field,
16+
expr: expr,
17+
}
18+
}
19+
20+
// StringSelector returns the field selector of string type.
21+
func (f *FieldHandler) StringSelector() string {
22+
return f.selector
23+
}
24+
25+
// FieldSelector returns the field selector of FieldSelector type.
26+
func (f *FieldHandler) FieldSelector() FieldSelector {
27+
return FieldSelector(f.selector)
28+
}
29+
30+
// Value returns the field value.
31+
// NOTE:
32+
// If initZero==true, initialize nil pointer to zero value
33+
func (f *FieldHandler) Value(initZero bool) reflect.Value {
34+
return f.field.reflectValueGetter(f.expr.ptr, initZero)
35+
}
36+
37+
// EvalFuncs returns the tag expression eval functions.
38+
func (f *FieldHandler) EvalFuncs() map[ExprSelector]func() interface{} {
39+
targetTagExpr, _ := f.expr.checkout(f.selector)
40+
evals := make(map[ExprSelector]func() interface{}, len(f.field.exprs))
41+
for k, v := range f.field.exprs {
42+
expr := v
43+
exprSelector := ExprSelector(k)
44+
evals[exprSelector] = func() interface{} {
45+
return expr.run(exprSelector.Name(), targetTagExpr)
46+
}
47+
}
48+
return evals
49+
}
50+
51+
// StructField returns the field StructField object.
52+
func (f *FieldHandler) StructField() reflect.StructField {
53+
return f.field.structField
54+
}
55+
56+
// ExprHandler expr handler
57+
type ExprHandler struct {
58+
base string
59+
path string
60+
selector string
61+
expr *TagExpr
62+
targetExpr *TagExpr
63+
}
64+
65+
func newExprHandler(te, tte *TagExpr, base, es string) *ExprHandler {
66+
return &ExprHandler{
67+
base: base,
68+
selector: es,
69+
expr: te,
70+
targetExpr: tte,
71+
}
72+
}
73+
74+
// TagExpr returns the *TagExpr.
75+
func (e *ExprHandler) TagExpr() *TagExpr {
76+
return e.expr
77+
}
78+
79+
// StringSelector returns the expression selector of string type.
80+
func (e *ExprHandler) StringSelector() string {
81+
return e.selector
82+
}
83+
84+
// ExprSelector returns the expression selector of ExprSelector type.
85+
func (e *ExprHandler) ExprSelector() ExprSelector {
86+
return ExprSelector(e.selector)
87+
}
88+
89+
// Path returns the path description of the expression.
90+
func (e *ExprHandler) Path() string {
91+
if e.path == "" {
92+
if e.targetExpr.path == "" {
93+
e.path = e.selector
94+
} else {
95+
e.path = e.targetExpr.path + FieldSeparator + e.selector
96+
}
97+
}
98+
return e.path
99+
}
100+
101+
// Eval evaluate the value of the struct tag expression.
102+
// NOTE:
103+
// result types: float64, string, bool, nil
104+
func (e *ExprHandler) Eval() interface{} {
105+
return e.expr.s.exprs[e.selector].run(e.base, e.targetExpr)
106+
}
107+
108+
// EvalFloat evaluates the value of the struct tag expression.
109+
// NOTE:
110+
// If the expression value type is not float64, return 0.
111+
func (e *ExprHandler) EvalFloat() float64 {
112+
r, _ := e.Eval().(float64)
113+
return r
114+
}
115+
116+
// EvalString evaluates the value of the struct tag expression.
117+
// NOTE:
118+
// If the expression value type is not string, return "".
119+
func (e *ExprHandler) EvalString() string {
120+
r, _ := e.Eval().(string)
121+
return r
122+
}
123+
124+
// EvalBool evaluates the value of the struct tag expression.
125+
// NOTE:
126+
// If the expression value is not 0, '' or nil, return true.
127+
func (e *ExprHandler) EvalBool() bool {
128+
return FakeBool(e.Eval())
129+
}

‎tagexpr.go

+7-16
Original file line numberDiff line numberDiff line change
@@ -659,23 +659,23 @@ type TagExpr struct {
659659
path string
660660
}
661661

662-
// EvalFloat evaluate the value of the struct tag expression by the selector expression.
662+
// EvalFloat evaluates the value of the struct tag expression by the selector expression.
663663
// NOTE:
664664
// If the expression value type is not float64, return 0.
665665
func (t *TagExpr) EvalFloat(exprSelector string) float64 {
666666
r, _ := t.Eval(exprSelector).(float64)
667667
return r
668668
}
669669

670-
// EvalString evaluate the value of the struct tag expression by the selector expression.
670+
// EvalString evaluates the value of the struct tag expression by the selector expression.
671671
// NOTE:
672672
// If the expression value type is not string, return "".
673673
func (t *TagExpr) EvalString(exprSelector string) string {
674674
r, _ := t.Eval(exprSelector).(string)
675675
return r
676676
}
677677

678-
// EvalBool evaluate the value of the struct tag expression by the selector expression.
678+
// EvalBool evaluates the value of the struct tag expression by the selector expression.
679679
// NOTE:
680680
// If the expression value is not 0, '' or nil, return true.
681681
func (t *TagExpr) EvalBool(exprSelector string) bool {
@@ -721,7 +721,7 @@ func (t *TagExpr) RangeFields(fn func(*FieldHandler) bool) bool {
721721
return true
722722
}
723723

724-
// Eval evaluate the value of the struct tag expression by the selector expression.
724+
// Eval evaluates the value of the struct tag expression by the selector expression.
725725
// NOTE:
726726
// format: fieldName, fieldName.exprName, fieldName1.fieldName2.exprName1
727727
// result types: float64, string, bool, nil
@@ -752,25 +752,16 @@ func (t *TagExpr) Eval(exprSelector string) interface{} {
752752
// When fn returns false, interrupt traversal and return false.
753753
// NOTE:
754754
// eval result types: float64, string, bool, nil
755-
func (t *TagExpr) Range(fn func(path string, es ExprSelector, eval func() interface{}) error) error {
755+
func (t *TagExpr) Range(fn func(*ExprHandler) error) error {
756756
var err error
757757
if list := t.s.exprSelectorList; len(list) > 0 {
758-
exprs := t.s.exprs
759758
for _, es := range list {
760759
dir, base := splitFieldSelector(es)
761760
targetTagExpr, err := t.checkout(dir)
762761
if err != nil {
763762
continue
764763
}
765-
var path string
766-
if targetTagExpr.path == "" {
767-
path = es
768-
} else {
769-
path = targetTagExpr.path + FieldSeparator + es
770-
}
771-
err = fn(path, ExprSelector(es), func() interface{} {
772-
return exprs[es].run(base, targetTagExpr)
773-
})
764+
err = fn(newExprHandler(t, targetTagExpr, base, es))
774765
if err != nil {
775766
return err
776767
}
@@ -866,7 +857,7 @@ func (t *TagExpr) Range(fn func(path string, es ExprSelector, eval func() interf
866857
return nil
867858
}
868859

869-
func (t *TagExpr) subRange(omitNil bool, path string, value reflect.Value, fn func(string, ExprSelector, func() interface{}) error) error {
860+
func (t *TagExpr) subRange(omitNil bool, path string, value reflect.Value, fn func(*ExprHandler) error) error {
870861
return t.s.vm.subRunAll(omitNil, path, value, func(te *TagExpr, err error) error {
871862
if err != nil {
872863
return err

‎tagexpr_test.go

+33-29
Original file line numberDiff line numberDiff line change
@@ -226,10 +226,11 @@ func Test(t *testing.T) {
226226
t.Fatalf("Eval Serial: %d, selector: %q, got: %v, expect: %v", i, selector, val, value)
227227
}
228228
}
229-
tagExpr.Range(func(path string, es ExprSelector, eval func() interface{}) error {
229+
tagExpr.Range(func(eh *ExprHandler) error {
230+
es := eh.ExprSelector()
230231
t.Logf("Range selector: %s, field: %q exprName: %q", es, es.Field(), es.Name())
231232
value := c.tests[es.String()]
232-
val := eval()
233+
val := eh.Eval()
233234
if !reflect.DeepEqual(val, value) {
234235
t.Fatalf("Range NO: %d, selector: %q, got: %v, expect: %v", i, es, val, value)
235236
}
@@ -542,10 +543,11 @@ func TestOperator(t *testing.T) {
542543
t.Fatalf("Eval NO: %d, selector: %q, got: %v, expect: %v", i, selector, val, value)
543544
}
544545
}
545-
tagExpr.Range(func(path string, es ExprSelector, eval func() interface{}) error {
546+
tagExpr.Range(func(eh *ExprHandler) error {
547+
es := eh.ExprSelector()
546548
t.Logf("Range selector: %s, field: %q exprName: %q", es, es.Field(), es.Name())
547549
value := c.tests[es.String()]
548-
val := eval()
550+
val := eh.Eval()
549551
if !reflect.DeepEqual(val, value) {
550552
t.Fatalf("Range NO: %d, selector: %q, got: %v, expect: %v", i, es, val, value)
551553
}
@@ -575,9 +577,10 @@ func TestStruct(t *testing.T) {
575577
assert.Equal(t, "xxx", expr.EvalString("B.C3"))
576578
assert.Equal(t, "xxx", expr.EvalString("B.C"))
577579
assert.Equal(t, "xxx", expr.EvalString("B.C.D.X"))
578-
expr.Range(func(path string, es ExprSelector, eval func() interface{}) error {
579-
t.Logf("Range selector: %s, field: %q exprName: %q", path, es.Field(), es.Name())
580-
if eval().(string) != "xxx" {
580+
expr.Range(func(eh *ExprHandler) error {
581+
es := eh.ExprSelector()
582+
t.Logf("Range selector: %s, field: %q exprName: %q", es, es.Field(), es.Name())
583+
if eh.Eval().(string) != "xxx" {
581584
t.FailNow()
582585
}
583586
return nil
@@ -669,8 +672,9 @@ func TestStruct3(t *testing.T) {
669672
if expr.EvalString("XBlock.BlockType") != "BlockType" {
670673
t.Fatal(expr.EvalString("XBlock.BlockType"))
671674
}
672-
err := expr.Range(func(path string, es ExprSelector, eval func() interface{}) error {
673-
t.Logf("Range selector: %s, field: %q exprName: %q, eval: %v", path, es.Field(), es.Name(), eval())
675+
err := expr.Range(func(eh *ExprHandler) error {
676+
es := eh.ExprSelector()
677+
t.Logf("Range selector: %s, field: %q exprName: %q, eval: %v", eh.Path(), es.Field(), es.Name(), eh.Eval())
674678
return nil
675679
})
676680
assert.NoError(t, err)
@@ -685,10 +689,10 @@ func TestNilField(t *testing.T) {
685689
}
686690
vm := New("tagexpr")
687691
te := vm.MustRun(P{})
688-
te.Range(func(path string, es ExprSelector, eval func() interface{}) error {
689-
r := eval()
692+
te.Range(func(eh *ExprHandler) error {
693+
r := eh.Eval()
690694
if r != nil {
691-
t.Fatal(path, r)
695+
t.Fatal(eh.Path(), r)
692696
}
693697
return nil
694698
})
@@ -701,10 +705,10 @@ func TestNilField(t *testing.T) {
701705
Nil1: new(int),
702706
Nil2: new(int),
703707
}
704-
vm.MustRun(g).Range(func(path string, es ExprSelector, eval func() interface{}) error {
705-
r, ok := eval().(bool)
708+
vm.MustRun(g).Range(func(eh *ExprHandler) error {
709+
r, ok := eh.Eval().(bool)
706710
if !ok || !r {
707-
t.Fatal(path, r)
711+
t.Fatal(eh.Path(), r)
708712
}
709713
return nil
710714
})
@@ -733,12 +737,12 @@ func TestNilField(t *testing.T) {
733737
SI: []interface{}{&M{X: "nn"}},
734738
}
735739
var cnt int
736-
vm.MustRun(n).Range(func(path string, es ExprSelector, eval func() interface{}) error {
737-
r, ok := eval().(bool)
740+
vm.MustRun(n).Range(func(eh *ExprHandler) error {
741+
r, ok := eh.Eval().(bool)
738742
if !ok || !r {
739-
t.Fatal(path, r)
743+
t.Fatal(eh.Path(), r)
740744
}
741-
t.Log("path:", path, "es:", es, "val:", r)
745+
t.Log("path:", eh.Path(), "es:", eh.ExprSelector(), "val:", r)
742746
cnt++
743747
return nil
744748
})
@@ -781,11 +785,11 @@ func TestDeepNested(t *testing.T) {
781785
expectValue := [...]interface{}{"I:address", nil, "A:address", nil, "X:address"}
782786
var i int
783787
vm := New("tagexpr")
784-
vm.MustRun(data).Range(func(path string, es ExprSelector, eval func() interface{}) error {
785-
assert.Equal(t, expectKey[i], path)
786-
assert.Equal(t, expectValue[i], eval())
788+
vm.MustRun(data).Range(func(eh *ExprHandler) error {
789+
assert.Equal(t, expectKey[i], eh.Path())
790+
assert.Equal(t, expectValue[i], eh.Eval())
787791
i++
788-
t.Log(path, es, eval())
792+
t.Log(eh.Path(), eh.ExprSelector(), eh.Eval())
789793
return nil
790794
})
791795
assert.Equal(t, 5, i)
@@ -809,16 +813,16 @@ func TestIssue3(t *testing.T) {
809813
},
810814
}
811815
vm := New("vd")
812-
err := vm.MustRun(a).Range(func(path string, es ExprSelector, eval func() interface{}) error {
813-
switch path {
816+
err := vm.MustRun(a).Range(func(eh *ExprHandler) error {
817+
switch eh.Path() {
814818
case "F1.Index":
815-
assert.Equal(t, float64(1), eval(), path)
819+
assert.Equal(t, float64(1), eh.Eval(), eh.Path())
816820
case "F2.Index":
817-
assert.Equal(t, nil, eval(), path)
821+
assert.Equal(t, nil, eh.Eval(), eh.Path())
818822
case "F1.P":
819-
assert.Equal(t, true, eval(), path)
823+
assert.Equal(t, true, eh.Eval(), eh.Path())
820824
case "F2.P":
821-
assert.Equal(t, false, eval(), path)
825+
assert.Equal(t, false, eh.Eval(), eh.Path())
822826
}
823827
return nil
824828
})

‎validator/validator.go

+7-9
Original file line numberDiff line numberDiff line change
@@ -77,21 +77,19 @@ func (v *Validator) Validate(value interface{}, checkAll ...bool) error {
7777
return io.EOF
7878
}
7979
nilParentFields := make(map[string]bool, 16)
80-
err = te.Range(func(path string, es tagexpr.ExprSelector, eval func() interface{}) error {
81-
s := es.String()
82-
if strings.Contains(s, tagexpr.ExprNameSeparator) {
80+
err = te.Range(func(eh *tagexpr.ExprHandler) error {
81+
if strings.Contains(eh.StringSelector(), tagexpr.ExprNameSeparator) {
8382
return nil
8483
}
85-
valid := tagexpr.FakeBool(eval())
86-
if valid {
84+
if eh.EvalBool() {
8785
return nil
8886
}
8987
// Ignore this error if the value of the parent is nil
90-
if pfs, ok := es.ParentField(); ok {
88+
if pfs, ok := eh.ExprSelector().ParentField(); ok {
9189
if nilParentFields[pfs] {
9290
return nil
9391
}
94-
if fh, ok := te.Field(pfs); ok {
92+
if fh, ok := eh.TagExpr().Field(pfs); ok {
9593
v := fh.Value(false)
9694
if !v.IsValid() || (v.Kind() == reflect.Ptr && v.IsNil()) {
9795
nilParentFields[pfs] = true
@@ -100,8 +98,8 @@ func (v *Validator) Validate(value interface{}, checkAll ...bool) error {
10098
}
10199
}
102100
errInfos = append(errInfos, &ErrInfo{
103-
selector: s,
104-
path: path,
101+
selector: eh.StringSelector(),
102+
path: eh.Path(),
105103
te: te,
106104
})
107105
if all {

‎validator/validator_test.go

+26-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package validator_test
22

33
import (
4+
"encoding/json"
45
"testing"
56

67
vd "github.com/bytedance/go-tagexpr/validator"
@@ -120,7 +121,7 @@ func TestIssue4(t *testing.T) {
120121
a = &A{F1: new(C)}
121122
assert.EqualError(t, v.Validate(a), "invalid parameter: F1.Index")
122123

123-
a = &A{F2: map[string]*C{"": new(C)}}
124+
a = &A{F2: map[string]*C{"": nil}}
124125
assert.EqualError(t, v.Validate(a), "invalid parameter: F2{}.Index")
125126

126127
a = &A{F3: []*C{new(C)}}
@@ -142,6 +143,29 @@ func TestIssue4(t *testing.T) {
142143
D []*D
143144
}
144145
b.F1 = new(C)
145-
e := &E{D: []*D{}}
146+
e := &E{D: []*D{nil}}
146147
assert.NoError(t, v.Validate(e))
147148
}
149+
150+
func TestIssue5(t *testing.T) {
151+
type SubSheet struct {
152+
}
153+
type CopySheet struct {
154+
Source *SubSheet `json:"source" vd:"$!=nil"`
155+
Destination *SubSheet `json:"destination" vd:"$!=nil"`
156+
}
157+
type UpdateSheetsRequest struct {
158+
CopySheet *CopySheet `json:"copySheet"`
159+
}
160+
type BatchUpdateSheetRequestArg struct {
161+
Requests []*UpdateSheetsRequest `json:"requests"`
162+
}
163+
b := `{"requests": [{}]}`
164+
var data BatchUpdateSheetRequestArg
165+
err := json.Unmarshal([]byte(b), &data)
166+
assert.NoError(t, err)
167+
assert.Equal(t, 1, len(data.Requests))
168+
assert.Nil(t, data.Requests[0].CopySheet)
169+
v := vd.New("vd")
170+
assert.NoError(t, v.Validate(data))
171+
}

0 commit comments

Comments
 (0)
Please sign in to comment.