Skip to content

Commit c97eada

Browse files
committed
Improve Map and Slice to support scalar elements
1 parent 600d973 commit c97eada

6 files changed

+67
-25
lines changed

builtin.go

+18-7
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,16 @@ func Nested[T any](f func(T) Validator) Validator {
5050

5151
// Map is a composite validator factory used to create a validator, which will
5252
// do the validation per the schemas associated with a map.
53-
func Map[T map[K]V, K comparable, V any](f func(T) map[K]Schema) Validator {
53+
func Map[T map[K]V, K comparable, V any](f func(T) map[K]Validator) Validator {
5454
return Func(func(field *Field) (errs Errors) {
5555
v, ok := field.Value.(T)
5656
if !ok {
5757
return NewUnsupportedErrors(field, "Map")
5858
}
5959

60-
schemas := f(v)
61-
for k, s := range schemas {
60+
validators := f(v)
61+
for k, validator := range validators {
62+
s := toSchema(v[k], validator)
6263
err := validateSchema(s, field, func(name string) string {
6364
return name + fmt.Sprintf("[%v]", k)
6465
})
@@ -72,15 +73,16 @@ func Map[T map[K]V, K comparable, V any](f func(T) map[K]Schema) Validator {
7273

7374
// Slice is a composite validator factory used to create a validator, which will
7475
// do the validation per the schemas associated with a slice.
75-
func Slice[T ~[]E, E any](f func(T) []Schema) Validator {
76+
func Slice[T ~[]E, E any](f func(T) []Validator) Validator {
7677
return Func(func(field *Field) (errs Errors) {
7778
v, ok := field.Value.(T)
7879
if !ok {
7980
return NewUnsupportedErrors(field, "Slice")
8081
}
8182

82-
schemas := f(v)
83-
for i, s := range schemas {
83+
validators := f(v)
84+
for i, validator := range validators {
85+
s := toSchema(v[i], validator)
8486
err := validateSchema(s, field, func(name string) string {
8587
return name + "[" + strconv.Itoa(i) + "]"
8688
})
@@ -93,7 +95,7 @@ func Slice[T ~[]E, E any](f func(T) []Schema) Validator {
9395
}
9496

9597
// Array is an alias of Slice.
96-
func Array[T ~[]E, E any](f func(T) []Schema) Validator {
98+
func Array[T ~[]E, E any](f func(T) []Validator) Validator {
9799
return Slice[T](f)
98100
}
99101

@@ -561,6 +563,15 @@ func Match(re *regexp.Regexp) (mv *MessageValidator) {
561563
return
562564
}
563565

566+
// toSchema converts the given validator to a Schema if it's not already.
567+
func toSchema(value any, validator Validator) Schema {
568+
s, ok := validator.(Schema)
569+
if !ok {
570+
s = Value(value, validator)
571+
}
572+
return s
573+
}
574+
564575
// validateSchema do the validation per the given schema, which is associated
565576
// with the given field.
566577
func validateSchema(schema Schema, field *Field, prefixFunc func(string) string) (errs Errors) {

builtin_test.go

+39-8
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func TestMap(t *testing.T) {
7373
{
7474
name: "nil map",
7575
value: map[string]Stat(nil),
76-
validator: v.Map(func(m map[string]Stat) map[string]v.Schema {
76+
validator: v.Map(func(m map[string]Stat) map[string]v.Validator {
7777
return nil
7878
}),
7979
errs: nil,
@@ -84,8 +84,8 @@ func TestMap(t *testing.T) {
8484
"visitor": {Count: 0},
8585
"visit": {Count: 0},
8686
},
87-
validator: v.Map(func(m map[string]Stat) map[string]v.Schema {
88-
schemas := make(map[string]v.Schema)
87+
validator: v.Map(func(m map[string]Stat) map[string]v.Validator {
88+
schemas := make(map[string]v.Validator)
8989
for k, s := range m {
9090
schemas[k] = v.Schema{
9191
v.F("count", s.Count): v.Nonzero[int](),
@@ -104,8 +104,8 @@ func TestMap(t *testing.T) {
104104
"visitor": {Count: 1},
105105
"visit": {Count: 2},
106106
},
107-
validator: v.Map(func(m map[string]Stat) map[string]v.Schema {
108-
schemas := make(map[string]v.Schema)
107+
validator: v.Map(func(m map[string]Stat) map[string]v.Validator {
108+
schemas := make(map[string]v.Validator)
109109
for k, s := range m {
110110
schemas[k] = v.Schema{
111111
v.F("count", s.Count): v.Nonzero[int](),
@@ -115,6 +115,24 @@ func TestMap(t *testing.T) {
115115
}),
116116
errs: nil,
117117
},
118+
{
119+
name: "int map",
120+
value: map[string]int{
121+
"k1": 1,
122+
"k2": 2,
123+
"k3": 3,
124+
},
125+
validator: v.Map(func(m map[string]int) map[string]v.Validator {
126+
schemas := make(map[string]v.Validator)
127+
for k := range m {
128+
schemas[k] = v.Range[int](1, 2)
129+
}
130+
return schemas
131+
}),
132+
errs: v.Errors{
133+
v.NewError("stats[k3]", v.ErrInvalid, "is not between the given range"),
134+
},
135+
},
118136
}
119137
for _, c := range cases {
120138
t.Run(c.name, func(t *testing.T) {
@@ -143,7 +161,7 @@ func TestSlice(t *testing.T) {
143161
{
144162
name: "nil slice",
145163
value: []Comment(nil),
146-
validator: v.Slice(func(s []Comment) (schemas []v.Schema) {
164+
validator: v.Slice(func(s []Comment) (schemas []v.Validator) {
147165
return nil
148166
}),
149167
errs: nil,
@@ -154,7 +172,7 @@ func TestSlice(t *testing.T) {
154172
{Content: "", CreatedAt: time.Time{}},
155173
},
156174

157-
validator: v.Slice(func(s []Comment) (schemas []v.Schema) {
175+
validator: v.Slice(func(s []Comment) (schemas []v.Validator) {
158176
for _, c := range s {
159177
schemas = append(schemas, v.Schema{
160178
v.F("content", c.Content): v.Nonzero[string](),
@@ -171,7 +189,7 @@ func TestSlice(t *testing.T) {
171189
{
172190
name: "nil slice",
173191
value: []Comment(nil),
174-
validator: v.Slice(func(s []Comment) (schemas []v.Schema) {
192+
validator: v.Slice(func(s []Comment) (schemas []v.Validator) {
175193
for _, c := range s {
176194
schemas = append(schemas, v.Schema{
177195
v.F("content", c.Content): v.Nonzero[string](),
@@ -182,6 +200,19 @@ func TestSlice(t *testing.T) {
182200
}),
183201
errs: nil,
184202
},
203+
{
204+
name: "int slice",
205+
value: []int{1, 2, 3},
206+
validator: v.Slice(func(s []int) (schemas []v.Validator) {
207+
for range s {
208+
schemas = append(schemas, v.Range[int](1, 2))
209+
}
210+
return
211+
}),
212+
errs: v.Errors{
213+
v.NewError("comments[2]", v.ErrInvalid, "is not between the given range"),
214+
},
215+
},
185216
}
186217
for _, c := range cases {
187218
t.Run(c.name, func(t *testing.T) {

example_nested_struct_map_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ func makeSchema1(p *Person1) v.Schema {
2020
return v.Schema{
2121
v.F("name", p.Name): v.LenString(1, 5),
2222
v.F("age", p.Age): v.Nonzero[int](),
23-
v.F("family", p.Family): v.Map(func(m map[string]*Member) map[string]v.Schema {
24-
schemas := make(map[string]v.Schema)
23+
v.F("family", p.Family): v.Map(func(m map[string]*Member) map[string]v.Validator {
24+
schemas := make(map[string]v.Validator)
2525
for relation, member := range m {
2626
schemas[relation] = v.Schema{
2727
v.F("name", member.Name): v.LenString(10, 15).Msg("is too long"),

example_nested_struct_slice_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func makeSchema4(p *Person4) v.Schema {
2020
return v.Schema{
2121
v.F("name", p.Name): v.LenString(1, 5),
2222
v.F("age", p.Age): v.Nonzero[int](),
23-
v.F("phones", p.Phones): v.Slice(func(s []*Phone) (schemas []v.Schema) {
23+
v.F("phones", p.Phones): v.Slice(func(s []*Phone) (schemas []v.Validator) {
2424
for _, phone := range s {
2525
schemas = append(schemas, v.Schema{
2626
v.F("number", phone.Number): v.Nonzero[string](),

example_simple_map_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ func Example_simpleMap() {
1111
"foo": 0,
1212
"bar": 1,
1313
}
14-
err := v.Validate(v.Value(ages, v.Map(func(m map[string]int) map[string]v.Schema {
15-
schemas := make(map[string]v.Schema)
16-
for name, age := range m {
17-
schemas[name] = v.Value(age, v.Nonzero[int]())
14+
err := v.Validate(v.Value(ages, v.Map(func(m map[string]int) map[string]v.Validator {
15+
schemas := make(map[string]v.Validator)
16+
for name := range m {
17+
schemas[name] = v.Nonzero[int]()
1818
}
1919
return schemas
2020
})))

example_simple_slice_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import (
88

99
func Example_simpleSlice() {
1010
names := []string{"", "foo"}
11-
err := v.Validate(v.Value(names, v.Slice(func(s []string) (schemas []v.Schema) {
12-
for _, name := range s {
13-
schemas = append(schemas, v.Value(name, v.Nonzero[string]()))
11+
err := v.Validate(v.Value(names, v.Slice(func(s []string) (schemas []v.Validator) {
12+
for range s {
13+
schemas = append(schemas, v.Nonzero[string]())
1414
}
1515
return schemas
1616
})))

0 commit comments

Comments
 (0)