github.com/bytedance/go-tagexpr@v2.7.5-0.20210114074101-de5b8743ad85+incompatible/validator/validator.go (about) 1 // Package validator is a powerful validator that supports struct tag expression. 2 // 3 // Copyright 2019 Bytedance Inc. All Rights Reserved. 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 package validator 17 18 import ( 19 "errors" 20 "io" 21 "reflect" 22 "strings" 23 _ "unsafe" 24 25 tagexpr "github.com/bytedance/go-tagexpr" 26 ) 27 28 const ( 29 // MatchExprName the name of the expression used for validation 30 MatchExprName = tagexpr.DefaultExprName 31 // ErrMsgExprName the name of the expression used to specify the message 32 // returned when validation failed 33 ErrMsgExprName = "msg" 34 ) 35 36 // Validator struct fields validator 37 type Validator struct { 38 vm *tagexpr.VM 39 errFactory func(failPath, msg string) error 40 } 41 42 // New creates a struct fields validator. 43 func New(tagName string) *Validator { 44 v := &Validator{ 45 vm: tagexpr.New(tagName), 46 errFactory: defaultErrorFactory, 47 } 48 return v 49 } 50 51 // VM returns the struct tag expression interpreter. 52 func (v *Validator) VM() *tagexpr.VM { 53 return v.vm 54 } 55 56 // Validate validates whether the fields of value is valid. 57 // NOTE: 58 // If checkAll=true, validate all the error. 59 func (v *Validator) Validate(value interface{}, checkAll ...bool) error { 60 var all bool 61 if len(checkAll) > 0 { 62 all = checkAll[0] 63 } 64 var errs = make([]error, 0, 8) 65 v.vm.RunAny(value, func(te *tagexpr.TagExpr, err error) error { 66 if err != nil { 67 errs = append(errs, err) 68 if all { 69 return nil 70 } 71 return io.EOF 72 } 73 nilParentFields := make(map[string]bool, 16) 74 err = te.Range(func(eh *tagexpr.ExprHandler) error { 75 if strings.Contains(eh.StringSelector(), tagexpr.ExprNameSeparator) { 76 return nil 77 } 78 r := eh.Eval() 79 if r == nil { 80 return nil 81 } 82 rerr, ok := r.(error) 83 if !ok && tagexpr.FakeBool(r) { 84 return nil 85 } 86 // Ignore this error if the value of the parent is nil 87 if pfs, ok := eh.ExprSelector().ParentField(); ok { 88 if nilParentFields[pfs] { 89 return nil 90 } 91 if fh, ok := eh.TagExpr().Field(pfs); ok { 92 v := fh.Value(false) 93 if !v.IsValid() || (v.Kind() == reflect.Ptr && v.IsNil()) { 94 nilParentFields[pfs] = true 95 return nil 96 } 97 } 98 } 99 msg := eh.TagExpr().EvalString(eh.StringSelector() + tagexpr.ExprNameSeparator + ErrMsgExprName) 100 if msg == "" && rerr != nil { 101 msg = rerr.Error() 102 } 103 errs = append(errs, v.errFactory(eh.Path(), msg)) 104 if all { 105 return nil 106 } 107 return io.EOF 108 }) 109 if err != nil && !all { 110 return err 111 } 112 return nil 113 }) 114 switch len(errs) { 115 case 0: 116 return nil 117 case 1: 118 return errs[0] 119 default: 120 var errStr string 121 for _, e := range errs { 122 errStr += e.Error() + "\t" 123 } 124 return errors.New(errStr[:len(errStr)-1]) 125 } 126 } 127 128 // SetErrorFactory customizes the factory of validation error. 129 // NOTE: 130 // If errFactory==nil, the default is used 131 func (v *Validator) SetErrorFactory(errFactory func(failPath, msg string) error) *Validator { 132 if errFactory == nil { 133 errFactory = defaultErrorFactory 134 } 135 v.errFactory = errFactory 136 return v 137 } 138 139 // Error validate error 140 type Error struct { 141 FailPath, Msg string 142 } 143 144 // Error implements error interface. 145 func (e *Error) Error() string { 146 if e.Msg != "" { 147 return e.Msg 148 } 149 return "invalid parameter: " + e.FailPath 150 } 151 152 //go:linkname defaultErrorFactory validator.defaultErrorFactory 153 //go:nosplit 154 func defaultErrorFactory(failPath, msg string) error { 155 return &Error{ 156 FailPath: failPath, 157 Msg: msg, 158 } 159 }