Skip to content

Commit

Permalink
Improve error messages
Browse files Browse the repository at this point in the history
  • Loading branch information
RussellLuo committed Jun 26, 2024
1 parent 1e8a7c1 commit bb82618
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 46 deletions.
95 changes: 57 additions & 38 deletions builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ func Nested[T any](f func(T) Validator) Validator {
return Func(func(field *Field) Errors {
v, ok := field.Value.(T)
if !ok {
return NewUnsupportedErrors(field, "Nested")
var want T
return NewUnsupportedErrors("Nested", field, want)
}

return f(v).Validate(field)
Expand All @@ -58,7 +59,8 @@ func EachMap[T map[K]V, K comparable, V any](validator Validator) Validator {
return Func(func(field *Field) (errs Errors) {
v, ok := field.Value.(T)
if !ok {
return NewUnsupportedErrors(field, "EachMap")
var want T
return NewUnsupportedErrors("EachMap", field, want)
}

for k := range v {
Expand All @@ -84,7 +86,8 @@ func EachSlice[T ~[]E, E any](validator Validator) Validator {
return Func(func(field *Field) (errs Errors) {
v, ok := field.Value.(T)
if !ok {
return NewUnsupportedErrors(field, "EachSlice")
var want T
return NewUnsupportedErrors("EachSlice", field, want)
}

for i := range v {
Expand All @@ -106,7 +109,8 @@ func Map[T map[K]V, K comparable, V any](f func(T) map[K]Validator) Validator {
return Func(func(field *Field) (errs Errors) {
v, ok := field.Value.(T)
if !ok {
return NewUnsupportedErrors(field, "Map")
var want T
return NewUnsupportedErrors("Map", field, want)
}

validators := f(v)
Expand All @@ -129,7 +133,8 @@ func Slice[T ~[]E, E any](f func(T) []Validator) Validator {
return Func(func(field *Field) (errs Errors) {
v, ok := field.Value.(T)
if !ok {
return NewUnsupportedErrors(field, "Slice")
var want T
return NewUnsupportedErrors("Slice", field, want)
}

validators := f(v)
Expand Down Expand Up @@ -237,7 +242,7 @@ func Not(validator Validator) (mv *MessageValidator) {
Validator: Func(func(field *Field) Errors {
errs := validator.Validate(field)
if len(errs) == 0 {
return NewErrors(field.Name, ErrInvalid, mv.Message)
return NewInvalidErrors(field, mv.Message)
}

var newErrs Errors
Expand All @@ -261,11 +266,12 @@ func Is[T any](f func(T) bool) (mv *MessageValidator) {
Validator: Func(func(field *Field) Errors {
v, ok := field.Value.(T)
if !ok {
return NewUnsupportedErrors(field, "Is")
var want T
return NewUnsupportedErrors("Is", field, want)
}

if !f(v) {
return NewErrors(field.Name, ErrInvalid, mv.Message)
return NewInvalidErrors(field, mv.Message)
}
return nil
}),
Expand All @@ -281,12 +287,13 @@ func Nonzero[T comparable]() (mv *MessageValidator) {
Validator: Func(func(field *Field) Errors {
v, ok := field.Value.(T)
if !ok {
return NewUnsupportedErrors(field, "Nonzero")
var want T
return NewUnsupportedErrors("Nonzero", field, want)
}

var zero T
if v == zero {
return NewErrors(field.Name, ErrInvalid, mv.Message)
return NewInvalidErrors(field, mv.Message)
}
return nil
}),
Expand All @@ -302,12 +309,13 @@ func Zero[T comparable]() (mv *MessageValidator) {
Validator: Func(func(field *Field) Errors {
v, ok := field.Value.(T)
if !ok {
return NewUnsupportedErrors(field, "Nonzero")
var want T
return NewUnsupportedErrors("Zero", field, want)
}

var zero T
if v != zero {
return NewErrors(field.Name, ErrInvalid, mv.Message)
return NewInvalidErrors(field, mv.Message)
}
return nil
}),
Expand All @@ -331,12 +339,13 @@ func LenString(min, max int) (mv *MessageValidator) {
Validator: Func(func(field *Field) Errors {
v, ok := field.Value.(string)
if !ok {
return NewUnsupportedErrors(field, "LenString")
var want string
return NewUnsupportedErrors("LenString", field, want)
}

l := len(v)
if l < min || l > max {
return NewErrors(field.Name, ErrInvalid, mv.Message)
return NewInvalidErrors(field, mv.Message)
}
return nil
}),
Expand All @@ -352,12 +361,13 @@ func LenSlice[T ~[]E, E any](min, max int) (mv *MessageValidator) {
Validator: Func(func(field *Field) Errors {
v, ok := field.Value.(T)
if !ok {
return NewUnsupportedErrors(field, "LenSlice")
var want T
return NewUnsupportedErrors("LenSlice", field, want)
}

l := len(v)
if l < min || l > max {
return NewErrors(field.Name, ErrInvalid, mv.Message)
return NewInvalidErrors(field, mv.Message)
}
return nil
}),
Expand All @@ -381,11 +391,11 @@ func RuneCount(min, max int) (mv *MessageValidator) {
l := utf8.RuneCount(v)
valid = l >= min && l <= max
default:
return NewUnsupportedErrors(field, "RuneCount")
return NewUnsupportedErrors("RuneCount", field, "", []byte(nil))
}

if !valid {
return NewErrors(field.Name, ErrInvalid, mv.Message)
return NewInvalidErrors(field, mv.Message)
}
return nil
}),
Expand All @@ -401,11 +411,12 @@ func Eq[T comparable](value T) (mv *MessageValidator) {
Validator: Func(func(field *Field) Errors {
v, ok := field.Value.(T)
if !ok {
return NewUnsupportedErrors(field, "Eq")
var want T
return NewUnsupportedErrors("Eq", field, want)
}

if v != value {
return NewErrors(field.Name, ErrInvalid, mv.Message)
return NewInvalidErrors(field, mv.Message)
}
return nil
}),
Expand All @@ -421,11 +432,12 @@ func Ne[T comparable](value T) (mv *MessageValidator) {
Validator: Func(func(field *Field) Errors {
v, ok := field.Value.(T)
if !ok {
return NewUnsupportedErrors(field, "Ne")
var want T
return NewUnsupportedErrors("Ne", field, want)
}

if v == value {
return NewErrors(field.Name, ErrInvalid, mv.Message)
return NewInvalidErrors(field, mv.Message)
}
return nil
}),
Expand All @@ -441,11 +453,12 @@ func Gt[T constraints.Ordered](value T) (mv *MessageValidator) {
Validator: Func(func(field *Field) Errors {
v, ok := field.Value.(T)
if !ok {
return NewUnsupportedErrors(field, "Gt")
var want T
return NewUnsupportedErrors("Gt", field, want)
}

if v <= value {
return NewErrors(field.Name, ErrInvalid, mv.Message)
return NewInvalidErrors(field, mv.Message)
}
return nil
}),
Expand All @@ -461,11 +474,12 @@ func Gte[T constraints.Ordered](value T) (mv *MessageValidator) {
Validator: Func(func(field *Field) Errors {
v, ok := field.Value.(T)
if !ok {
return NewUnsupportedErrors(field, "Gte")
var want T
return NewUnsupportedErrors("Gte", field, want)
}

if v < value {
return NewErrors(field.Name, ErrInvalid, mv.Message)
return NewInvalidErrors(field, mv.Message)
}
return nil
}),
Expand All @@ -481,11 +495,12 @@ func Lt[T constraints.Ordered](value T) (mv *MessageValidator) {
Validator: Func(func(field *Field) Errors {
v, ok := field.Value.(T)
if !ok {
return NewUnsupportedErrors(field, "Lt")
var want T
return NewUnsupportedErrors("Lt", field, want)
}

if v >= value {
return NewErrors(field.Name, ErrInvalid, mv.Message)
return NewInvalidErrors(field, mv.Message)
}
return nil
}),
Expand All @@ -501,11 +516,12 @@ func Lte[T constraints.Ordered](value T) (mv *MessageValidator) {
Validator: Func(func(field *Field) Errors {
v, ok := field.Value.(T)
if !ok {
return NewUnsupportedErrors(field, "Lte")
var want T
return NewUnsupportedErrors("Lte", field, want)
}

if v > value {
return NewErrors(field.Name, ErrInvalid, mv.Message)
return NewInvalidErrors(field, mv.Message)
}
return nil
}),
Expand All @@ -521,11 +537,12 @@ func Range[T constraints.Ordered](min, max T) (mv *MessageValidator) {
Validator: Func(func(field *Field) Errors {
v, ok := field.Value.(T)
if !ok {
return NewUnsupportedErrors(field, "Range")
var want T
return NewUnsupportedErrors("Range", field, want)
}

if v < min || v > max {
return NewErrors(field.Name, ErrInvalid, mv.Message)
return NewInvalidErrors(field, mv.Message)
}
return nil
}),
Expand All @@ -541,7 +558,8 @@ func In[T comparable](values ...T) (mv *MessageValidator) {
Validator: Func(func(field *Field) Errors {
v, ok := field.Value.(T)
if !ok {
return NewUnsupportedErrors(field, "In")
var want T
return NewUnsupportedErrors("In", field, want)
}

valid := false
Expand All @@ -553,7 +571,7 @@ func In[T comparable](values ...T) (mv *MessageValidator) {
}

if !valid {
return NewErrors(field.Name, ErrInvalid, mv.Message)
return NewInvalidErrors(field, mv.Message)
}
return nil
}),
Expand All @@ -569,7 +587,8 @@ func Nin[T comparable](values ...T) (mv *MessageValidator) {
Validator: Func(func(field *Field) Errors {
v, ok := field.Value.(T)
if !ok {
return NewUnsupportedErrors(field, "Nin")
var want T
return NewUnsupportedErrors("Nin", field, want)
}

valid := true
Expand All @@ -581,7 +600,7 @@ func Nin[T comparable](values ...T) (mv *MessageValidator) {
}

if !valid {
return NewErrors(field.Name, ErrInvalid, mv.Message)
return NewInvalidErrors(field, mv.Message)
}
return nil
}),
Expand All @@ -603,11 +622,11 @@ func Match(re *regexp.Regexp) (mv *MessageValidator) {
case []byte:
valid = re.Match(v)
default:
return NewUnsupportedErrors(field, "Match")
return NewUnsupportedErrors("Match", field, "", []byte(nil))
}

if !valid {
return NewErrors(field.Name, ErrInvalid, mv.Message)
return NewInvalidErrors(field, mv.Message)
}
return nil
}),
Expand Down
10 changes: 5 additions & 5 deletions builtin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ func TestNot(t *testing.T) {
v.Schema{
v.F("value", []string{"foo"}): v.Not(v.Eq("foo")),
},
v.NewErrors("value", v.ErrUnsupported, "cannot use validator `Eq` on type []string"),
v.NewErrors("value", v.ErrUnsupported, "Eq expected string but got []string"),
},
{
v.Schema{
Expand Down Expand Up @@ -691,7 +691,7 @@ func TestLenString(t *testing.T) {
{
value: 0,
validator: v.LenString(1, 2),
errs: v.NewErrors("value", v.ErrUnsupported, "cannot use validator `LenString` on type int"),
errs: v.NewErrors("value", v.ErrUnsupported, "LenString expected string but got int"),
},
{
value: "",
Expand Down Expand Up @@ -728,7 +728,7 @@ func TestLenSlice(t *testing.T) {
{
value: "",
validator: v.LenSlice[[]string](1, 2),
errs: v.NewErrors("value", v.ErrUnsupported, "cannot use validator `LenSlice` on type string"),
errs: v.NewErrors("value", v.ErrUnsupported, "LenSlice expected []string but got string"),
},
{
value: []int(nil),
Expand Down Expand Up @@ -780,7 +780,7 @@ func TestRuneCount(t *testing.T) {
{
value: 0,
validator: v.RuneCount(1, 2),
errs: v.NewErrors("value", v.ErrUnsupported, "cannot use validator `RuneCount` on type int"),
errs: v.NewErrors("value", v.ErrUnsupported, "RuneCount expected string or []byte but got int"),
},
{
value: "",
Expand Down Expand Up @@ -1241,7 +1241,7 @@ func TestMatch(t *testing.T) {
{
value: 0,
validator: v.Match(regexp.MustCompile(``)),
errs: v.NewErrors("value", v.ErrUnsupported, "cannot use validator `Match` on type int"),
errs: v.NewErrors("value", v.ErrUnsupported, "Match expected string or []byte but got int"),
},
{
value: "x13012345678",
Expand Down
17 changes: 15 additions & 2 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,21 @@ func NewErrors(field, kind, message string) Errors {
return []Error{NewError(field, kind, message)}
}

func NewUnsupportedErrors(field *Field, validatorName string) Errors {
return NewErrors(field.Name, ErrUnsupported, fmt.Sprintf("cannot use validator `%s` on type %T", validatorName, field.Value))
func NewUnsupportedErrors(validatorName string, field *Field, want ...any) Errors {
var wantTypes []string
for _, w := range want {
t := fmt.Sprintf("%T", w)
if t == "[]uint8" {
t = "[]byte" // []uint8 => []byte
}
wantTypes = append(wantTypes, t)
}
expected := strings.Join(wantTypes, " or ")
return NewErrors(field.Name, ErrUnsupported, fmt.Sprintf("%s expected %s but got %T", validatorName, expected, field.Value))
}

func NewInvalidErrors(field *Field, msg string) Errors {
return NewErrors(field.Name, ErrInvalid, msg)
}

func (e *Errors) Append(errs ...Error) {
Expand Down
3 changes: 2 additions & 1 deletion example_customizations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import (
func mapNonzero(field *v.Field) v.Errors {
value, ok := field.Value.(map[string]time.Time)
if !ok {
return v.NewUnsupportedErrors(field, "mapNonzero")
var want map[string]time.Time
return v.NewUnsupportedErrors("mapNonzero", field, want)
}
if len(value) == 0 {
return v.NewErrors(field.Name, v.ErrInvalid, "is zero valued")
Expand Down
1 change: 1 addition & 0 deletions example_nested_struct_map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func makeSchema1(p *Person1) v.Schema {
}

// The equivalent implementation using Map.
//
//nolint:golint,unused
func makeSchema1_Map(p *Person1) v.Schema {
return v.Schema{
Expand Down
Loading

0 comments on commit bb82618

Please sign in to comment.