github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/depends/kit/validator/validator__struct.go (about) 1 package validator 2 3 import ( 4 "context" 5 "go/ast" 6 "reflect" 7 8 "github.com/machinefi/w3bstream/pkg/depends/kit/validator/errors" 9 "github.com/machinefi/w3bstream/pkg/depends/x/reflectx" 10 "github.com/machinefi/w3bstream/pkg/depends/x/typesx" 11 ) 12 13 func NewStructValidator(tagKey string) *Struct { 14 return &Struct{ 15 tag: tagKey, 16 validators: map[string]Validator{}, 17 } 18 } 19 20 type ckStructTagKey struct{} 21 22 func ContextWithTagKey(ctx context.Context, namedTagKey string) context.Context { 23 return context.WithValue(ctx, ckStructTagKey{}, namedTagKey) 24 } 25 26 func TagKeyFromContext(ctx context.Context) string { 27 v := ctx.Value(ckStructTagKey{}) 28 if v != nil { 29 if namedTagKey, ok := v.(string); ok { 30 return namedTagKey 31 } 32 } 33 return "" 34 } 35 36 type Struct struct { 37 tag string 38 validators map[string]Validator // validators: field name => validator 39 } 40 41 func init() { DefaultFactory.Register(&Struct{}) } 42 43 func (Struct) Names() []string { return []string{"struct"} } 44 45 func (vs *Struct) Validate(v interface{}) error { 46 switch rv := v.(type) { 47 case reflect.Value: 48 return vs.ValidateReflectValue(rv) 49 default: 50 return vs.ValidateReflectValue(reflect.ValueOf(v)) 51 } 52 } 53 54 func (vs *Struct) ValidateReflectValue(rv reflect.Value) error { 55 errs := errors.NewErrorSet() 56 vs.validate(rv, errs) 57 return errs.Err() 58 } 59 60 func (vs *Struct) validate(rv reflect.Value, errs *errors.ErrorSet) { 61 for i := 0; i < rv.NumField(); i++ { 62 f := rv.Type().Field(i) 63 fv := rv.Field(i) 64 name, _, exists := typesx.FieldDisplayName(f.Tag, vs.tag, f.Name) 65 66 if !ast.IsExported(f.Name) || name == "-" { 67 continue 68 } 69 70 ft_ := reflectx.DeRef(f.Type) 71 isSub := ft_.Kind() == reflect.Struct 72 73 if f.Anonymous && isSub && !exists { 74 if fv.Kind() == reflect.Ptr && fv.IsNil() { 75 fv = reflectx.New(ft_) 76 } 77 vs.validate(fv, errs) 78 continue 79 } 80 81 if validator, ok := vs.validators[f.Name]; ok { 82 err := validator.Validate(fv) 83 errs.AddErr(err, name) 84 } 85 } 86 } 87 88 func (vs *Struct) TypeCheck(r *Rule) error { 89 if r.Type.Kind() != reflect.Struct { 90 return errors.NewUnsupportedTypeError(r.String(), vs.String()) 91 } 92 return nil 93 } 94 95 const ( 96 TagValidate = "validate" 97 TagDefault = "default" 98 TagErrMsg = "errMsg" 99 ) 100 101 func (vs *Struct) New(ctx context.Context, r *Rule) (Validator, error) { 102 if err := vs.TypeCheck(r); err != nil { 103 return nil, err 104 } 105 106 tag := TagKeyFromContext(ctx) 107 if r.Rule != nil && len(r.Params) > 0 { 108 tag = string(r.Params[0].Bytes()) 109 } 110 if tag == "" { 111 tag = vs.tag 112 } 113 114 var ( 115 ret = NewStructValidator(tag) 116 errs = errors.NewErrorSet() 117 compiler = FactoryFromContext(ctx) 118 ) 119 ctx = ContextWithTagKey(ctx, ret.tag) 120 121 typesx.EachField( 122 r.Type, 123 ret.tag, 124 func(field typesx.StructField, display string, omitempty bool) bool { 125 tagv := field.Tag().Get(TagValidate) 126 127 if tagv == "" && typesx.DeRef(field.Type()).Kind() == reflect.Struct { 128 _, ok := typesx.EncodingTextMarshalerTypeReplacer(field.Type()) 129 if !ok { 130 tagv = ret.String() 131 } 132 } 133 134 sub, err := compiler.Compile( 135 ContextWithTagKey(ctx, tag), 136 []byte(tagv), 137 field.Type(), 138 func(rule Modifier) { 139 if omitempty { 140 rule.SetOptional(omitempty) 141 } 142 if dftv, ok := field.Tag().Lookup(TagDefault); ok { 143 rule.SetDefaultValue([]byte(dftv)) 144 } 145 if errMsg, ok := field.Tag().Lookup(TagErrMsg); ok { 146 rule.SetErrMsg([]byte(errMsg)) 147 } 148 }, 149 ) 150 151 if err != nil { 152 errs.AddErr(err, field.Name()) 153 return true 154 } 155 156 if sub != nil { 157 ret.validators[field.Name()] = sub 158 } 159 return true 160 }, 161 ) 162 163 return ret, errs.Err() 164 } 165 166 func (vs *Struct) String() string { 167 return "@" + vs.Names()[0] + "<" + vs.tag + ">" 168 }