Skip to content

Commit a4b2773

Browse files
committed
feat: implement ValidatorValuer interface feature
# Conflicts: # validator_test.go
1 parent c3fc72e commit a4b2773

File tree

2 files changed

+246
-0
lines changed

2 files changed

+246
-0
lines changed

util.go

+28
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ import (
99
"time"
1010
)
1111

12+
// ValidatorValuer is an interface that allows you to expose a method on a type
13+
// (including generic types) that returns a value that is supposed to be validated.
14+
type ValidatorValuer interface {
15+
// ValidatorValue returns the value that is supposed to be validated.
16+
ValidatorValue() any
17+
}
18+
1219
// extractTypeInternal gets the actual underlying type of field value.
1320
// It will dive into pointers, customTypes and return you the
1421
// underlying value and it's kind.
@@ -23,6 +30,13 @@ BEGIN:
2330
return current, reflect.Ptr, nullable
2431
}
2532

33+
if current.CanInterface() {
34+
if v, ok := current.Interface().(ValidatorValuer); ok {
35+
current = reflect.ValueOf(v.ValidatorValue())
36+
goto BEGIN
37+
}
38+
}
39+
2640
current = current.Elem()
2741
goto BEGIN
2842

@@ -34,6 +48,13 @@ BEGIN:
3448
return current, reflect.Interface, nullable
3549
}
3650

51+
if current.CanInterface() {
52+
if v, ok := current.Interface().(ValidatorValuer); ok {
53+
current = reflect.ValueOf(v.ValidatorValue())
54+
goto BEGIN
55+
}
56+
}
57+
3758
current = current.Elem()
3859
goto BEGIN
3960

@@ -42,6 +63,13 @@ BEGIN:
4263

4364
default:
4465

66+
if current.CanInterface() {
67+
if v, ok := current.Interface().(ValidatorValuer); ok {
68+
current = reflect.ValueOf(v.ValidatorValue())
69+
goto BEGIN
70+
}
71+
}
72+
4573
if v.v.hasCustomFuncs {
4674
if fn, ok := v.v.customFuncs[current.Type()]; ok {
4775
current = reflect.ValueOf(fn(current))

validator_test.go

+218
Original file line numberDiff line numberDiff line change
@@ -14293,3 +14293,221 @@ func TestValidateFn(t *testing.T) {
1429314293
Equal(t, fe.Tag(), "validateFn")
1429414294
})
1429514295
}
14296+
14297+
type ValidatorValuerTypeWithPointerReceiver[T any] struct {
14298+
Data T
14299+
}
14300+
14301+
func (t *ValidatorValuerTypeWithPointerReceiver[T]) ValidatorValue() any {
14302+
return t.Data
14303+
}
14304+
14305+
type ValidatorValuerTypeWithValueReceiver[T any] struct {
14306+
Data T
14307+
}
14308+
14309+
func (t ValidatorValuerTypeWithValueReceiver[T]) ValidatorValue() any {
14310+
return t.Data
14311+
}
14312+
14313+
func TestValidatorValuerInterface(t *testing.T) {
14314+
t.Run("parent as ValidatorValuer (not called)", func(t *testing.T) {
14315+
errs := New().Struct(&ValidatorValuerTypeWithPointerReceiver[SubTest]{})
14316+
AssertError(t, errs,
14317+
"ValidatorValuerTypeWithPointerReceiver[github.com/go-playground/validator/v10.SubTest].Data.Test",
14318+
"ValidatorValuerTypeWithPointerReceiver[github.com/go-playground/validator/v10.SubTest].Data.Test",
14319+
"Test", "Test", "required")
14320+
})
14321+
t.Run("pointer parent, pointer nested, pointer receiver (called)", func(t *testing.T) {
14322+
type Parent struct {
14323+
Nested *ValidatorValuerTypeWithPointerReceiver[SubTest] `validate:"required"`
14324+
}
14325+
14326+
errs := New().Struct(&Parent{})
14327+
AssertError(t, errs, "Parent.Nested", "Parent.Nested", "Nested", "Nested", "required")
14328+
14329+
errs = New().Struct(&Parent{
14330+
Nested: &ValidatorValuerTypeWithPointerReceiver[SubTest]{},
14331+
})
14332+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14333+
14334+
errs = New().Struct(&Parent{
14335+
Nested: &ValidatorValuerTypeWithPointerReceiver[SubTest]{
14336+
Data: SubTest{
14337+
Test: "Test",
14338+
},
14339+
},
14340+
})
14341+
if errs != nil {
14342+
t.Fatalf("Expected no error, got: %v", errs)
14343+
}
14344+
})
14345+
t.Run("pointer parent, pointer nested, value receiver (called)", func(t *testing.T) {
14346+
type Parent struct {
14347+
Nested *ValidatorValuerTypeWithValueReceiver[SubTest] `validate:"required"`
14348+
}
14349+
14350+
errs := New().Struct(&Parent{})
14351+
AssertError(t, errs, "Parent.Nested", "Parent.Nested", "Nested", "Nested", "required")
14352+
14353+
errs = New().Struct(&Parent{
14354+
Nested: &ValidatorValuerTypeWithValueReceiver[SubTest]{},
14355+
})
14356+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14357+
14358+
errs = New().Struct(&Parent{
14359+
Nested: &ValidatorValuerTypeWithValueReceiver[SubTest]{
14360+
Data: SubTest{
14361+
Test: "Test",
14362+
},
14363+
},
14364+
})
14365+
if errs != nil {
14366+
t.Fatalf("Expected no error, got: %v", errs)
14367+
}
14368+
})
14369+
t.Run("pointer parent, value nested, pointer receiver (not called)", func(t *testing.T) {
14370+
type Parent struct {
14371+
Nested ValidatorValuerTypeWithPointerReceiver[SubTest] `validate:"required"`
14372+
}
14373+
14374+
errs := New().Struct(&Parent{})
14375+
AssertError(t, errs, "Parent.Nested.Data.Test", "Parent.Nested.Data.Test", "Test", "Test", "required")
14376+
14377+
errs = New().Struct(&Parent{
14378+
Nested: ValidatorValuerTypeWithPointerReceiver[SubTest]{},
14379+
})
14380+
AssertError(t, errs, "Parent.Nested.Data.Test", "Parent.Nested.Data.Test", "Test", "Test", "required")
14381+
14382+
errs = New().Struct(&Parent{
14383+
Nested: ValidatorValuerTypeWithPointerReceiver[SubTest]{
14384+
Data: SubTest{
14385+
Test: "Test",
14386+
},
14387+
},
14388+
})
14389+
if errs != nil {
14390+
t.Fatalf("Expected no error, got: %v", errs)
14391+
}
14392+
})
14393+
t.Run("pointer parent, value nested, value receiver (called)", func(t *testing.T) {
14394+
type Parent struct {
14395+
Nested ValidatorValuerTypeWithValueReceiver[SubTest] `validate:"required"`
14396+
}
14397+
14398+
errs := New().Struct(&Parent{})
14399+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14400+
14401+
errs = New().Struct(&Parent{
14402+
Nested: ValidatorValuerTypeWithValueReceiver[SubTest]{},
14403+
})
14404+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14405+
14406+
errs = New().Struct(&Parent{
14407+
Nested: ValidatorValuerTypeWithValueReceiver[SubTest]{
14408+
Data: SubTest{
14409+
Test: "Test",
14410+
},
14411+
},
14412+
})
14413+
if errs != nil {
14414+
t.Fatalf("Expected no error, got: %v", errs)
14415+
}
14416+
})
14417+
t.Run("value parent, pointer nested, pointer receiver (called)", func(t *testing.T) {
14418+
type Parent struct {
14419+
Nested *ValidatorValuerTypeWithPointerReceiver[SubTest] `validate:"required"`
14420+
}
14421+
14422+
errs := New().Struct(Parent{})
14423+
AssertError(t, errs, "Parent.Nested", "Parent.Nested", "Nested", "Nested", "required")
14424+
14425+
errs = New().Struct(Parent{
14426+
Nested: &ValidatorValuerTypeWithPointerReceiver[SubTest]{},
14427+
})
14428+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14429+
14430+
errs = New().Struct(Parent{
14431+
Nested: &ValidatorValuerTypeWithPointerReceiver[SubTest]{
14432+
Data: SubTest{
14433+
Test: "Test",
14434+
},
14435+
},
14436+
})
14437+
if errs != nil {
14438+
t.Fatalf("Expected no error, got: %v", errs)
14439+
}
14440+
})
14441+
t.Run("value parent, pointer nested, value receiver (called)", func(t *testing.T) {
14442+
type Parent struct {
14443+
Nested *ValidatorValuerTypeWithValueReceiver[SubTest] `validate:"required"`
14444+
}
14445+
14446+
errs := New().Struct(Parent{})
14447+
AssertError(t, errs, "Parent.Nested", "Parent.Nested", "Nested", "Nested", "required")
14448+
14449+
errs = New().Struct(Parent{
14450+
Nested: &ValidatorValuerTypeWithValueReceiver[SubTest]{},
14451+
})
14452+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14453+
14454+
errs = New().Struct(Parent{
14455+
Nested: &ValidatorValuerTypeWithValueReceiver[SubTest]{
14456+
Data: SubTest{
14457+
Test: "Test",
14458+
},
14459+
},
14460+
})
14461+
if errs != nil {
14462+
t.Fatalf("Expected no error, got: %v", errs)
14463+
}
14464+
})
14465+
t.Run("value parent, value nested, pointer receiver (not called)", func(t *testing.T) {
14466+
type Parent struct {
14467+
Nested ValidatorValuerTypeWithPointerReceiver[SubTest] `validate:"required"`
14468+
}
14469+
14470+
errs := New().Struct(Parent{})
14471+
AssertError(t, errs, "Parent.Nested.Data.Test", "Parent.Nested.Data.Test", "Test", "Test", "required")
14472+
14473+
errs = New().Struct(Parent{
14474+
Nested: ValidatorValuerTypeWithPointerReceiver[SubTest]{},
14475+
})
14476+
AssertError(t, errs, "Parent.Nested.Data.Test", "Parent.Nested.Data.Test", "Test", "Test", "required")
14477+
14478+
errs = New().Struct(Parent{
14479+
Nested: ValidatorValuerTypeWithPointerReceiver[SubTest]{
14480+
Data: SubTest{
14481+
Test: "Test",
14482+
},
14483+
},
14484+
})
14485+
if errs != nil {
14486+
t.Fatalf("Expected no error, got: %v", errs)
14487+
}
14488+
})
14489+
t.Run("value parent, value nested, value receiver (called)", func(t *testing.T) {
14490+
type Parent struct {
14491+
Nested ValidatorValuerTypeWithValueReceiver[SubTest] `validate:"required"`
14492+
}
14493+
14494+
errs := New().Struct(Parent{})
14495+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14496+
14497+
errs = New().Struct(Parent{
14498+
Nested: ValidatorValuerTypeWithValueReceiver[SubTest]{},
14499+
})
14500+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14501+
14502+
errs = New().Struct(Parent{
14503+
Nested: ValidatorValuerTypeWithValueReceiver[SubTest]{
14504+
Data: SubTest{
14505+
Test: "Test",
14506+
},
14507+
},
14508+
})
14509+
if errs != nil {
14510+
t.Fatalf("Expected no error, got: %v", errs)
14511+
}
14512+
})
14513+
}

0 commit comments

Comments
 (0)