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  }