github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/depends/kit/validator/validator__slice.go (about) 1 package validator 2 3 import ( 4 "context" 5 "fmt" 6 "reflect" 7 8 "github.com/machinefi/w3bstream/pkg/depends/kit/validator/errors" 9 "github.com/machinefi/w3bstream/pkg/depends/kit/validator/rules" 10 ) 11 12 type Slice struct { 13 ElemValidator Validator 14 15 MinItems uint64 16 MaxItems *uint64 17 } 18 19 func init() { DefaultFactory.Register(&Slice{}) } 20 21 func (Slice) Names() []string { return []string{"slice", "array"} } 22 23 func (vs *Slice) Validate(v interface{}) error { 24 switch rv := v.(type) { 25 case reflect.Value: 26 return vs.ValidateReflectValue(rv) 27 default: 28 return vs.ValidateReflectValue(reflect.ValueOf(v)) 29 } 30 } 31 32 var TargetSliceLength = "slice length" 33 34 func (vs *Slice) ValidateReflectValue(rv reflect.Value) error { 35 length := uint64(0) 36 if !rv.IsNil() { 37 length = uint64(rv.Len()) 38 } 39 if length < vs.MinItems { 40 return &errors.OutOfRangeError{ 41 Target: TargetSliceLength, 42 Current: length, 43 Minimum: vs.MinItems, 44 } 45 } 46 if vs.MaxItems != nil && length > *vs.MaxItems { 47 return &errors.OutOfRangeError{ 48 Target: TargetSliceLength, 49 Current: length, 50 Maximum: vs.MaxItems, 51 } 52 } 53 54 if vs.ElemValidator != nil { 55 errs := errors.NewErrorSet() 56 for i := 0; i < rv.Len(); i++ { 57 err := vs.ElemValidator.Validate(rv.Index(i)) 58 if err != nil { 59 errs.AddErr(err, i) 60 } 61 } 62 return errs.Err() 63 } 64 return nil 65 } 66 67 func (Slice) New(ctx context.Context, r *Rule) (Validator, error) { 68 vs := &Slice{} 69 70 if r.ExclusiveMin || r.ExclusiveMax { 71 return nil, errors.NewSyntaxError( 72 "range mark of %s should not be `(` or `)`", vs.Names()[0], 73 ) 74 } 75 76 min, max, err := UintRuleRange(r, "length of slice", 64) 77 if err != nil { 78 return nil, err 79 } 80 vs.MinItems = min 81 vs.MaxItems = max 82 83 switch r.Type.Kind() { 84 case reflect.Array: 85 if r.Type.Len() != int(vs.MinItems) { 86 return nil, fmt.Errorf( 87 "length(%d) or rule should equal length(%d)", 88 vs.MinItems, r.Type.Len(), 89 ) 90 } 91 case reflect.Slice: 92 default: 93 return nil, errors.NewUnsupportedTypeError(r.String(), vs.String()) 94 } 95 96 elemValidator, err := SliceElementRule(ctx, r) 97 if err != nil { 98 return nil, err 99 } 100 vs.ElemValidator = elemValidator 101 102 return vs, vs.TypeCheck(r) 103 } 104 105 func (vs *Slice) TypeCheck(r *Rule) error { 106 switch r.Type.Kind() { 107 case reflect.Array: 108 if r.Type.Len() != int(vs.MinItems) { 109 return fmt.Errorf( 110 "length(%d) or r should equal length(%d) of array", 111 vs.MinItems, r.Type.Len(), 112 ) 113 } 114 case reflect.Slice: 115 default: 116 return errors.NewUnsupportedTypeError(r.String(), vs.String()) 117 } 118 return nil 119 } 120 121 func (vs *Slice) String() string { 122 rule := rules.NewRule(vs.Names()[0]) 123 124 if vs.ElemValidator != nil { 125 rule.Params = append( 126 rule.Params, 127 rules.NewLiteral([]byte(vs.ElemValidator.String())), 128 ) 129 } 130 131 rule.Range = RangeFromUint(vs.MinItems, vs.MaxItems) 132 133 return string(rule.Bytes()) 134 }