// Copyright 2017 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. package binding import ( "fmt" "reflect" "strings" "github.com/go-playground/validator/v10" ) type defaultValidator struct { validate *validator.Validate } type sliceValidateError []error // Error concatenates all error elements in sliceValidateError into a single string separated by \n. func (err sliceValidateError) Error() string { n := len(err) switch n { case 0: return "" default: var b strings.Builder if err[0] != nil { fmt.Fprintf(&b, "[%d]: %s", 0, err[0].Error()) } if n > 1 { for i := 1; i < n; i++ { if err[i] != nil { b.WriteString("\n") fmt.Fprintf(&b, "[%d]: %s", i, err[i].Error()) } } } return b.String() } } var _ StructValidator = &defaultValidator{} // ValidateStruct receives any kind of type, but only performed struct or pointer to struct type. func (v *defaultValidator) ValidateStruct(obj interface{}) error { if obj == nil { return nil } value := reflect.ValueOf(obj) switch value.Kind() { case reflect.Ptr: return v.ValidateStruct(value.Elem().Interface()) case reflect.Struct: return v.validateStruct(obj) case reflect.Slice, reflect.Array: count := value.Len() validateRet := make(sliceValidateError, 0) for i := 0; i < count; i++ { if err := v.ValidateStruct(value.Index(i).Interface()); err != nil { validateRet = append(validateRet, err) } } if len(validateRet) == 0 { return nil } return validateRet default: return nil } } // validateStruct receives struct type func (v *defaultValidator) validateStruct(obj interface{}) error { v.lazyInit() return v.validate.Struct(obj) } // Engine returns the underlying validator engine which powers the default // Validator instance. This is useful if you want to register custom validations // or struct level validations. See validator GoDoc for more info - // https://pkg.go.dev/github.com/go-playground/validator/v10 func (v *defaultValidator) Engine() interface{} { v.lazyInit() return v.validate } // lazyInit initialize the validate Settings, no need to use sync.once func (v *defaultValidator) lazyInit() { if v.validate == nil { v.validate = validator.New() v.validate.SetTagName("binding") } }