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  }