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  }