blob: 59fdf263c2c52c57fecf9158186140d3efb71ddd [file] [log] [blame]
package validate
import (
"errors"
"fmt"
"strings"
ut "github.com/go-playground/universal-translator"
valid "github.com/go-playground/validator"
)
var errorTranslator ut.Translator // no use but as an index
func registerErrorTranslator(_ ut.Translator) error { return nil }
// Validator validates data
// not safe, use it after initialized
type Validator struct {
rules map[string]*RegexValidateRule
valid *valid.Validate
}
// Validate validates the input data
func (v *Validator) Validate(i interface{}) error {
err := v.valid.Struct(i)
if err != nil {
return v.wrapError(err)
}
return nil
}
// converts the raw error into an easy-to-understand error
func (v *Validator) wrapError(err error) error {
validErr, ok := err.(valid.ValidationErrors)
if !ok {
return err
}
msgs := make([]string, len(validErr))
for i, ve := range validErr {
fe := ve.(valid.FieldError)
msgs[i] = fe.Translate(errorTranslator)
}
return errors.New("validate failed, " + strings.Join(msgs, " | "))
}
// RegisterRule registers a custom validate rule
func (v *Validator) RegisterRule(r *RegexValidateRule) error {
if r == nil {
return errors.New("empty regex validate rule")
}
v.rules[r.tag] = r
if err := v.valid.RegisterValidation(r.tag, r.validateFL); err != nil {
return err
}
return v.AddErrorTranslation4Tag(r.tag)
}
// translates raw errors to easy-to-understand messages
func (v *Validator) translateError(_ ut.Translator, fe valid.FieldError) string {
var rule string
if r, ok := v.rules[fe.Tag()]; ok {
rule = r.Explain()
} else {
rule = fe.Tag()
if len(fe.Param()) > 0 {
rule = rule + " = " + fe.Param()
}
}
return fmt.Sprintf("field: %s, rule: %s", fe.Namespace(), rule)
}
// AddErrorTranslation4Tag adds translation for the errors of some tag,
// to make the error easier to understand
func (v *Validator) AddErrorTranslation4Tag(tag string) error {
return v.valid.RegisterTranslation(tag,
errorTranslator,
registerErrorTranslator,
v.translateError)
}
// NewValidator news a validator
func NewValidator() *Validator {
return &Validator{
valid: valid.New(),
rules: make(map[string]*RegexValidateRule),
}
}