Skip to content

Commit 3a70956

Browse files
committed
feat: implement ValidatorValuer interface feature
1 parent 0540a5e commit 3a70956

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
@@ -14148,3 +14148,221 @@ func TestPrivateFieldsStruct(t *testing.T) {
1414814148
Equal(t, len(errs), tc.errorNum)
1414914149
}
1415014150
}
14151+
14152+
type ValidatorValuerTypeWithPointerReceiver[T any] struct {
14153+
Data T
14154+
}
14155+
14156+
func (t *ValidatorValuerTypeWithPointerReceiver[T]) ValidatorValue() any {
14157+
return t.Data
14158+
}
14159+
14160+
type ValidatorValuerTypeWithValueReceiver[T any] struct {
14161+
Data T
14162+
}
14163+
14164+
func (t ValidatorValuerTypeWithValueReceiver[T]) ValidatorValue() any {
14165+
return t.Data
14166+
}
14167+
14168+
func TestValidatorValuerInterface(t *testing.T) {
14169+
t.Run("parent as ValidatorValuer (not called)", func(t *testing.T) {
14170+
errs := New().Struct(&ValidatorValuerTypeWithPointerReceiver[SubTest]{})
14171+
AssertError(t, errs,
14172+
"ValidatorValuerTypeWithPointerReceiver[github.com/go-playground/validator/v10.SubTest].Data.Test",
14173+
"ValidatorValuerTypeWithPointerReceiver[github.com/go-playground/validator/v10.SubTest].Data.Test",
14174+
"Test", "Test", "required")
14175+
})
14176+
t.Run("pointer parent, pointer nested, pointer receiver (called)", func(t *testing.T) {
14177+
type Parent struct {
14178+
Nested *ValidatorValuerTypeWithPointerReceiver[SubTest] `validate:"required"`
14179+
}
14180+
14181+
errs := New().Struct(&Parent{})
14182+
AssertError(t, errs, "Parent.Nested", "Parent.Nested", "Nested", "Nested", "required")
14183+
14184+
errs = New().Struct(&Parent{
14185+
Nested: &ValidatorValuerTypeWithPointerReceiver[SubTest]{},
14186+
})
14187+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14188+
14189+
errs = New().Struct(&Parent{
14190+
Nested: &ValidatorValuerTypeWithPointerReceiver[SubTest]{
14191+
Data: SubTest{
14192+
Test: "Test",
14193+
},
14194+
},
14195+
})
14196+
if errs != nil {
14197+
t.Fatalf("Expected no error, got: %v", errs)
14198+
}
14199+
})
14200+
t.Run("pointer parent, pointer nested, value receiver (called)", func(t *testing.T) {
14201+
type Parent struct {
14202+
Nested *ValidatorValuerTypeWithValueReceiver[SubTest] `validate:"required"`
14203+
}
14204+
14205+
errs := New().Struct(&Parent{})
14206+
AssertError(t, errs, "Parent.Nested", "Parent.Nested", "Nested", "Nested", "required")
14207+
14208+
errs = New().Struct(&Parent{
14209+
Nested: &ValidatorValuerTypeWithValueReceiver[SubTest]{},
14210+
})
14211+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14212+
14213+
errs = New().Struct(&Parent{
14214+
Nested: &ValidatorValuerTypeWithValueReceiver[SubTest]{
14215+
Data: SubTest{
14216+
Test: "Test",
14217+
},
14218+
},
14219+
})
14220+
if errs != nil {
14221+
t.Fatalf("Expected no error, got: %v", errs)
14222+
}
14223+
})
14224+
t.Run("pointer parent, value nested, pointer receiver (not called)", func(t *testing.T) {
14225+
type Parent struct {
14226+
Nested ValidatorValuerTypeWithPointerReceiver[SubTest] `validate:"required"`
14227+
}
14228+
14229+
errs := New().Struct(&Parent{})
14230+
AssertError(t, errs, "Parent.Nested.Data.Test", "Parent.Nested.Data.Test", "Test", "Test", "required")
14231+
14232+
errs = New().Struct(&Parent{
14233+
Nested: ValidatorValuerTypeWithPointerReceiver[SubTest]{},
14234+
})
14235+
AssertError(t, errs, "Parent.Nested.Data.Test", "Parent.Nested.Data.Test", "Test", "Test", "required")
14236+
14237+
errs = New().Struct(&Parent{
14238+
Nested: ValidatorValuerTypeWithPointerReceiver[SubTest]{
14239+
Data: SubTest{
14240+
Test: "Test",
14241+
},
14242+
},
14243+
})
14244+
if errs != nil {
14245+
t.Fatalf("Expected no error, got: %v", errs)
14246+
}
14247+
})
14248+
t.Run("pointer parent, value nested, value receiver (called)", func(t *testing.T) {
14249+
type Parent struct {
14250+
Nested ValidatorValuerTypeWithValueReceiver[SubTest] `validate:"required"`
14251+
}
14252+
14253+
errs := New().Struct(&Parent{})
14254+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14255+
14256+
errs = New().Struct(&Parent{
14257+
Nested: ValidatorValuerTypeWithValueReceiver[SubTest]{},
14258+
})
14259+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14260+
14261+
errs = New().Struct(&Parent{
14262+
Nested: ValidatorValuerTypeWithValueReceiver[SubTest]{
14263+
Data: SubTest{
14264+
Test: "Test",
14265+
},
14266+
},
14267+
})
14268+
if errs != nil {
14269+
t.Fatalf("Expected no error, got: %v", errs)
14270+
}
14271+
})
14272+
t.Run("value parent, pointer nested, pointer receiver (called)", func(t *testing.T) {
14273+
type Parent struct {
14274+
Nested *ValidatorValuerTypeWithPointerReceiver[SubTest] `validate:"required"`
14275+
}
14276+
14277+
errs := New().Struct(Parent{})
14278+
AssertError(t, errs, "Parent.Nested", "Parent.Nested", "Nested", "Nested", "required")
14279+
14280+
errs = New().Struct(Parent{
14281+
Nested: &ValidatorValuerTypeWithPointerReceiver[SubTest]{},
14282+
})
14283+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14284+
14285+
errs = New().Struct(Parent{
14286+
Nested: &ValidatorValuerTypeWithPointerReceiver[SubTest]{
14287+
Data: SubTest{
14288+
Test: "Test",
14289+
},
14290+
},
14291+
})
14292+
if errs != nil {
14293+
t.Fatalf("Expected no error, got: %v", errs)
14294+
}
14295+
})
14296+
t.Run("value parent, pointer nested, value receiver (called)", func(t *testing.T) {
14297+
type Parent struct {
14298+
Nested *ValidatorValuerTypeWithValueReceiver[SubTest] `validate:"required"`
14299+
}
14300+
14301+
errs := New().Struct(Parent{})
14302+
AssertError(t, errs, "Parent.Nested", "Parent.Nested", "Nested", "Nested", "required")
14303+
14304+
errs = New().Struct(Parent{
14305+
Nested: &ValidatorValuerTypeWithValueReceiver[SubTest]{},
14306+
})
14307+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14308+
14309+
errs = New().Struct(Parent{
14310+
Nested: &ValidatorValuerTypeWithValueReceiver[SubTest]{
14311+
Data: SubTest{
14312+
Test: "Test",
14313+
},
14314+
},
14315+
})
14316+
if errs != nil {
14317+
t.Fatalf("Expected no error, got: %v", errs)
14318+
}
14319+
})
14320+
t.Run("value parent, value nested, pointer receiver (not called)", func(t *testing.T) {
14321+
type Parent struct {
14322+
Nested ValidatorValuerTypeWithPointerReceiver[SubTest] `validate:"required"`
14323+
}
14324+
14325+
errs := New().Struct(Parent{})
14326+
AssertError(t, errs, "Parent.Nested.Data.Test", "Parent.Nested.Data.Test", "Test", "Test", "required")
14327+
14328+
errs = New().Struct(Parent{
14329+
Nested: ValidatorValuerTypeWithPointerReceiver[SubTest]{},
14330+
})
14331+
AssertError(t, errs, "Parent.Nested.Data.Test", "Parent.Nested.Data.Test", "Test", "Test", "required")
14332+
14333+
errs = New().Struct(Parent{
14334+
Nested: ValidatorValuerTypeWithPointerReceiver[SubTest]{
14335+
Data: SubTest{
14336+
Test: "Test",
14337+
},
14338+
},
14339+
})
14340+
if errs != nil {
14341+
t.Fatalf("Expected no error, got: %v", errs)
14342+
}
14343+
})
14344+
t.Run("value parent, value nested, value receiver (called)", func(t *testing.T) {
14345+
type Parent struct {
14346+
Nested ValidatorValuerTypeWithValueReceiver[SubTest] `validate:"required"`
14347+
}
14348+
14349+
errs := New().Struct(Parent{})
14350+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14351+
14352+
errs = New().Struct(Parent{
14353+
Nested: ValidatorValuerTypeWithValueReceiver[SubTest]{},
14354+
})
14355+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14356+
14357+
errs = New().Struct(Parent{
14358+
Nested: ValidatorValuerTypeWithValueReceiver[SubTest]{
14359+
Data: SubTest{
14360+
Test: "Test",
14361+
},
14362+
},
14363+
})
14364+
if errs != nil {
14365+
t.Fatalf("Expected no error, got: %v", errs)
14366+
}
14367+
})
14368+
}

0 commit comments

Comments
 (0)