You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
272 lines
7.1 KiB
272 lines
7.1 KiB
package validator |
|
|
|
import ( |
|
"bytes" |
|
"fmt" |
|
"reflect" |
|
"strings" |
|
|
|
ut "github.com/go-playground/universal-translator" |
|
) |
|
|
|
const ( |
|
fieldErrMsg = "Key: '%s' Error:Field validation for '%s' failed on the '%s' tag" |
|
) |
|
|
|
// ValidationErrorsTranslations is the translation return type |
|
type ValidationErrorsTranslations map[string]string |
|
|
|
// InvalidValidationError describes an invalid argument passed to |
|
// `Struct`, `StructExcept`, StructPartial` or `Field` |
|
type InvalidValidationError struct { |
|
Type reflect.Type |
|
} |
|
|
|
// Error returns InvalidValidationError message |
|
func (e *InvalidValidationError) Error() string { |
|
|
|
if e.Type == nil { |
|
return "validator: (nil)" |
|
} |
|
|
|
return "validator: (nil " + e.Type.String() + ")" |
|
} |
|
|
|
// ValidationErrors is an array of FieldError's |
|
// for use in custom error messages post validation. |
|
type ValidationErrors []FieldError |
|
|
|
// Error is intended for use in development + debugging and not intended to be a production error message. |
|
// It allows ValidationErrors to subscribe to the Error interface. |
|
// All information to create an error message specific to your application is contained within |
|
// the FieldError found within the ValidationErrors array |
|
func (ve ValidationErrors) Error() string { |
|
|
|
buff := bytes.NewBufferString("") |
|
|
|
var fe *fieldError |
|
|
|
for i := 0; i < len(ve); i++ { |
|
|
|
fe = ve[i].(*fieldError) |
|
buff.WriteString(fe.Error()) |
|
buff.WriteString("\n") |
|
} |
|
|
|
return strings.TrimSpace(buff.String()) |
|
} |
|
|
|
// Translate translates all of the ValidationErrors |
|
func (ve ValidationErrors) Translate(ut ut.Translator) ValidationErrorsTranslations { |
|
|
|
trans := make(ValidationErrorsTranslations) |
|
|
|
var fe *fieldError |
|
|
|
for i := 0; i < len(ve); i++ { |
|
fe = ve[i].(*fieldError) |
|
|
|
// // in case an Anonymous struct was used, ensure that the key |
|
// // would be 'Username' instead of ".Username" |
|
// if len(fe.ns) > 0 && fe.ns[:1] == "." { |
|
// trans[fe.ns[1:]] = fe.Translate(ut) |
|
// continue |
|
// } |
|
|
|
trans[fe.ns] = fe.Translate(ut) |
|
} |
|
|
|
return trans |
|
} |
|
|
|
// FieldError contains all functions to get error details |
|
type FieldError interface { |
|
|
|
// returns the validation tag that failed. if the |
|
// validation was an alias, this will return the |
|
// alias name and not the underlying tag that failed. |
|
// |
|
// eg. alias "iscolor": "hexcolor|rgb|rgba|hsl|hsla" |
|
// will return "iscolor" |
|
Tag() string |
|
|
|
// returns the validation tag that failed, even if an |
|
// alias the actual tag within the alias will be returned. |
|
// If an 'or' validation fails the entire or will be returned. |
|
// |
|
// eg. alias "iscolor": "hexcolor|rgb|rgba|hsl|hsla" |
|
// will return "hexcolor|rgb|rgba|hsl|hsla" |
|
ActualTag() string |
|
|
|
// returns the namespace for the field error, with the tag |
|
// name taking precedence over the fields actual name. |
|
// |
|
// eg. JSON name "User.fname" |
|
// |
|
// See StructNamespace() for a version that returns actual names. |
|
// |
|
// NOTE: this field can be blank when validating a single primitive field |
|
// using validate.Field(...) as there is no way to extract it's name |
|
Namespace() string |
|
|
|
// returns the namespace for the field error, with the fields |
|
// actual name. |
|
// |
|
// eq. "User.FirstName" see Namespace for comparison |
|
// |
|
// NOTE: this field can be blank when validating a single primitive field |
|
// using validate.Field(...) as there is no way to extract it's name |
|
StructNamespace() string |
|
|
|
// returns the fields name with the tag name taking precedence over the |
|
// fields actual name. |
|
// |
|
// eq. JSON name "fname" |
|
// see ActualField for comparison |
|
Field() string |
|
|
|
// returns the fields actual name from the struct, when able to determine. |
|
// |
|
// eq. "FirstName" |
|
// see Field for comparison |
|
StructField() string |
|
|
|
// returns the actual fields value in case needed for creating the error |
|
// message |
|
Value() interface{} |
|
|
|
// returns the param value, in string form for comparison; this will also |
|
// help with generating an error message |
|
Param() string |
|
|
|
// Kind returns the Field's reflect Kind |
|
// |
|
// eg. time.Time's kind is a struct |
|
Kind() reflect.Kind |
|
|
|
// Type returns the Field's reflect Type |
|
// |
|
// // eg. time.Time's type is time.Time |
|
Type() reflect.Type |
|
|
|
// returns the FieldError's translated error |
|
// from the provided 'ut.Translator' and registered 'TranslationFunc' |
|
// |
|
// NOTE: is not registered translation can be found it returns the same |
|
// as calling fe.Error() |
|
Translate(ut ut.Translator) string |
|
} |
|
|
|
// compile time interface checks |
|
var _ FieldError = new(fieldError) |
|
var _ error = new(fieldError) |
|
|
|
// fieldError contains a single field's validation error along |
|
// with other properties that may be needed for error message creation |
|
// it complies with the FieldError interface |
|
type fieldError struct { |
|
v *Validate |
|
tag string |
|
actualTag string |
|
ns string |
|
structNs string |
|
fieldLen uint8 |
|
structfieldLen uint8 |
|
value interface{} |
|
param string |
|
kind reflect.Kind |
|
typ reflect.Type |
|
} |
|
|
|
// Tag returns the validation tag that failed. |
|
func (fe *fieldError) Tag() string { |
|
return fe.tag |
|
} |
|
|
|
// ActualTag returns the validation tag that failed, even if an |
|
// alias the actual tag within the alias will be returned. |
|
func (fe *fieldError) ActualTag() string { |
|
return fe.actualTag |
|
} |
|
|
|
// Namespace returns the namespace for the field error, with the tag |
|
// name taking precedence over the fields actual name. |
|
func (fe *fieldError) Namespace() string { |
|
return fe.ns |
|
} |
|
|
|
// StructNamespace returns the namespace for the field error, with the fields |
|
// actual name. |
|
func (fe *fieldError) StructNamespace() string { |
|
return fe.structNs |
|
} |
|
|
|
// Field returns the fields name with the tag name taking precedence over the |
|
// fields actual name. |
|
func (fe *fieldError) Field() string { |
|
|
|
return fe.ns[len(fe.ns)-int(fe.fieldLen):] |
|
// // return fe.field |
|
// fld := fe.ns[len(fe.ns)-int(fe.fieldLen):] |
|
|
|
// log.Println("FLD:", fld) |
|
|
|
// if len(fld) > 0 && fld[:1] == "." { |
|
// return fld[1:] |
|
// } |
|
|
|
// return fld |
|
} |
|
|
|
// returns the fields actual name from the struct, when able to determine. |
|
func (fe *fieldError) StructField() string { |
|
// return fe.structField |
|
return fe.structNs[len(fe.structNs)-int(fe.structfieldLen):] |
|
} |
|
|
|
// Value returns the actual fields value in case needed for creating the error |
|
// message |
|
func (fe *fieldError) Value() interface{} { |
|
return fe.value |
|
} |
|
|
|
// Param returns the param value, in string form for comparison; this will |
|
// also help with generating an error message |
|
func (fe *fieldError) Param() string { |
|
return fe.param |
|
} |
|
|
|
// Kind returns the Field's reflect Kind |
|
func (fe *fieldError) Kind() reflect.Kind { |
|
return fe.kind |
|
} |
|
|
|
// Type returns the Field's reflect Type |
|
func (fe *fieldError) Type() reflect.Type { |
|
return fe.typ |
|
} |
|
|
|
// Error returns the fieldError's error message |
|
func (fe *fieldError) Error() string { |
|
return fmt.Sprintf(fieldErrMsg, fe.ns, fe.Field(), fe.tag) |
|
} |
|
|
|
// Translate returns the FieldError's translated error |
|
// from the provided 'ut.Translator' and registered 'TranslationFunc' |
|
// |
|
// NOTE: is not registered translation can be found it returns the same |
|
// as calling fe.Error() |
|
func (fe *fieldError) Translate(ut ut.Translator) string { |
|
|
|
m, ok := fe.v.transTagFunc[ut] |
|
if !ok { |
|
return fe.Error() |
|
} |
|
|
|
fn, ok := m[fe.tag] |
|
if !ok { |
|
return fe.Error() |
|
} |
|
|
|
return fn(ut, fe) |
|
}
|
|
|