Skip to content

Commit 2996074

Browse files
committed
feat: implement Valuer interface feature
1 parent 5b31512 commit 2996074

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+
// Valuer 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 Valuer 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().(Valuer); 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().(Valuer); 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().(Valuer); 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
@@ -14319,3 +14319,221 @@ func TestValidateFn(t *testing.T) {
1431914319
Equal(t, fe.Tag(), "validateFn")
1432014320
})
1432114321
}
14322+
14323+
type ValuerTypeWithPointerReceiver[T any] struct {
14324+
Data T
14325+
}
14326+
14327+
func (t *ValuerTypeWithPointerReceiver[T]) ValidatorValue() any {
14328+
return t.Data
14329+
}
14330+
14331+
type ValuerTypeWithValueReceiver[T any] struct {
14332+
Data T
14333+
}
14334+
14335+
func (t ValuerTypeWithValueReceiver[T]) ValidatorValue() any {
14336+
return t.Data
14337+
}
14338+
14339+
func TestValuerInterface(t *testing.T) {
14340+
t.Run("parent as Valuer (not called)", func(t *testing.T) {
14341+
errs := New().Struct(&ValuerTypeWithPointerReceiver[SubTest]{})
14342+
AssertError(t, errs,
14343+
"ValuerTypeWithPointerReceiver[github.com/go-playground/validator/v10.SubTest].Data.Test",
14344+
"ValuerTypeWithPointerReceiver[github.com/go-playground/validator/v10.SubTest].Data.Test",
14345+
"Test", "Test", "required")
14346+
})
14347+
t.Run("pointer parent, pointer nested, pointer receiver (called)", func(t *testing.T) {
14348+
type Parent struct {
14349+
Nested *ValuerTypeWithPointerReceiver[SubTest] `validate:"required"`
14350+
}
14351+
14352+
errs := New().Struct(&Parent{})
14353+
AssertError(t, errs, "Parent.Nested", "Parent.Nested", "Nested", "Nested", "required")
14354+
14355+
errs = New().Struct(&Parent{
14356+
Nested: &ValuerTypeWithPointerReceiver[SubTest]{},
14357+
})
14358+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14359+
14360+
errs = New().Struct(&Parent{
14361+
Nested: &ValuerTypeWithPointerReceiver[SubTest]{
14362+
Data: SubTest{
14363+
Test: "Test",
14364+
},
14365+
},
14366+
})
14367+
if errs != nil {
14368+
t.Fatalf("Expected no error, got: %v", errs)
14369+
}
14370+
})
14371+
t.Run("pointer parent, pointer nested, value receiver (called)", func(t *testing.T) {
14372+
type Parent struct {
14373+
Nested *ValuerTypeWithValueReceiver[SubTest] `validate:"required"`
14374+
}
14375+
14376+
errs := New().Struct(&Parent{})
14377+
AssertError(t, errs, "Parent.Nested", "Parent.Nested", "Nested", "Nested", "required")
14378+
14379+
errs = New().Struct(&Parent{
14380+
Nested: &ValuerTypeWithValueReceiver[SubTest]{},
14381+
})
14382+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14383+
14384+
errs = New().Struct(&Parent{
14385+
Nested: &ValuerTypeWithValueReceiver[SubTest]{
14386+
Data: SubTest{
14387+
Test: "Test",
14388+
},
14389+
},
14390+
})
14391+
if errs != nil {
14392+
t.Fatalf("Expected no error, got: %v", errs)
14393+
}
14394+
})
14395+
t.Run("pointer parent, value nested, pointer receiver (not called)", func(t *testing.T) {
14396+
type Parent struct {
14397+
Nested ValuerTypeWithPointerReceiver[SubTest] `validate:"required"`
14398+
}
14399+
14400+
errs := New().Struct(&Parent{})
14401+
AssertError(t, errs, "Parent.Nested.Data.Test", "Parent.Nested.Data.Test", "Test", "Test", "required")
14402+
14403+
errs = New().Struct(&Parent{
14404+
Nested: ValuerTypeWithPointerReceiver[SubTest]{},
14405+
})
14406+
AssertError(t, errs, "Parent.Nested.Data.Test", "Parent.Nested.Data.Test", "Test", "Test", "required")
14407+
14408+
errs = New().Struct(&Parent{
14409+
Nested: ValuerTypeWithPointerReceiver[SubTest]{
14410+
Data: SubTest{
14411+
Test: "Test",
14412+
},
14413+
},
14414+
})
14415+
if errs != nil {
14416+
t.Fatalf("Expected no error, got: %v", errs)
14417+
}
14418+
})
14419+
t.Run("pointer parent, value nested, value receiver (called)", func(t *testing.T) {
14420+
type Parent struct {
14421+
Nested ValuerTypeWithValueReceiver[SubTest] `validate:"required"`
14422+
}
14423+
14424+
errs := New().Struct(&Parent{})
14425+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14426+
14427+
errs = New().Struct(&Parent{
14428+
Nested: ValuerTypeWithValueReceiver[SubTest]{},
14429+
})
14430+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14431+
14432+
errs = New().Struct(&Parent{
14433+
Nested: ValuerTypeWithValueReceiver[SubTest]{
14434+
Data: SubTest{
14435+
Test: "Test",
14436+
},
14437+
},
14438+
})
14439+
if errs != nil {
14440+
t.Fatalf("Expected no error, got: %v", errs)
14441+
}
14442+
})
14443+
t.Run("value parent, pointer nested, pointer receiver (called)", func(t *testing.T) {
14444+
type Parent struct {
14445+
Nested *ValuerTypeWithPointerReceiver[SubTest] `validate:"required"`
14446+
}
14447+
14448+
errs := New().Struct(Parent{})
14449+
AssertError(t, errs, "Parent.Nested", "Parent.Nested", "Nested", "Nested", "required")
14450+
14451+
errs = New().Struct(Parent{
14452+
Nested: &ValuerTypeWithPointerReceiver[SubTest]{},
14453+
})
14454+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14455+
14456+
errs = New().Struct(Parent{
14457+
Nested: &ValuerTypeWithPointerReceiver[SubTest]{
14458+
Data: SubTest{
14459+
Test: "Test",
14460+
},
14461+
},
14462+
})
14463+
if errs != nil {
14464+
t.Fatalf("Expected no error, got: %v", errs)
14465+
}
14466+
})
14467+
t.Run("value parent, pointer nested, value receiver (called)", func(t *testing.T) {
14468+
type Parent struct {
14469+
Nested *ValuerTypeWithValueReceiver[SubTest] `validate:"required"`
14470+
}
14471+
14472+
errs := New().Struct(Parent{})
14473+
AssertError(t, errs, "Parent.Nested", "Parent.Nested", "Nested", "Nested", "required")
14474+
14475+
errs = New().Struct(Parent{
14476+
Nested: &ValuerTypeWithValueReceiver[SubTest]{},
14477+
})
14478+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14479+
14480+
errs = New().Struct(Parent{
14481+
Nested: &ValuerTypeWithValueReceiver[SubTest]{
14482+
Data: SubTest{
14483+
Test: "Test",
14484+
},
14485+
},
14486+
})
14487+
if errs != nil {
14488+
t.Fatalf("Expected no error, got: %v", errs)
14489+
}
14490+
})
14491+
t.Run("value parent, value nested, pointer receiver (not called)", func(t *testing.T) {
14492+
type Parent struct {
14493+
Nested ValuerTypeWithPointerReceiver[SubTest] `validate:"required"`
14494+
}
14495+
14496+
errs := New().Struct(Parent{})
14497+
AssertError(t, errs, "Parent.Nested.Data.Test", "Parent.Nested.Data.Test", "Test", "Test", "required")
14498+
14499+
errs = New().Struct(Parent{
14500+
Nested: ValuerTypeWithPointerReceiver[SubTest]{},
14501+
})
14502+
AssertError(t, errs, "Parent.Nested.Data.Test", "Parent.Nested.Data.Test", "Test", "Test", "required")
14503+
14504+
errs = New().Struct(Parent{
14505+
Nested: ValuerTypeWithPointerReceiver[SubTest]{
14506+
Data: SubTest{
14507+
Test: "Test",
14508+
},
14509+
},
14510+
})
14511+
if errs != nil {
14512+
t.Fatalf("Expected no error, got: %v", errs)
14513+
}
14514+
})
14515+
t.Run("value parent, value nested, value receiver (called)", func(t *testing.T) {
14516+
type Parent struct {
14517+
Nested ValuerTypeWithValueReceiver[SubTest] `validate:"required"`
14518+
}
14519+
14520+
errs := New().Struct(Parent{})
14521+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14522+
14523+
errs = New().Struct(Parent{
14524+
Nested: ValuerTypeWithValueReceiver[SubTest]{},
14525+
})
14526+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
14527+
14528+
errs = New().Struct(Parent{
14529+
Nested: ValuerTypeWithValueReceiver[SubTest]{
14530+
Data: SubTest{
14531+
Test: "Test",
14532+
},
14533+
},
14534+
})
14535+
if errs != nil {
14536+
t.Fatalf("Expected no error, got: %v", errs)
14537+
}
14538+
})
14539+
}

0 commit comments

Comments
 (0)