Skip to content

Commit 05867e9

Browse files
committed
feat: implement Valuer interface feature
1 parent 6905468 commit 05867e9

File tree

2 files changed

+246
-0
lines changed

2 files changed

+246
-0
lines changed

util.go

Lines changed: 28 additions & 0 deletions
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

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14975,3 +14975,221 @@ func TestRequiredIfWithArrays(t *testing.T) {
1497514975
Equal(t, err, nil) // No error - Text has value
1497614976
})
1497714977
}
14978+
14979+
type ValuerTypeWithPointerReceiver[T any] struct {
14980+
Data T
14981+
}
14982+
14983+
func (t *ValuerTypeWithPointerReceiver[T]) ValidatorValue() any {
14984+
return t.Data
14985+
}
14986+
14987+
type ValuerTypeWithValueReceiver[T any] struct {
14988+
Data T
14989+
}
14990+
14991+
func (t ValuerTypeWithValueReceiver[T]) ValidatorValue() any {
14992+
return t.Data
14993+
}
14994+
14995+
func TestValuerInterface(t *testing.T) {
14996+
t.Run("parent as Valuer (not called)", func(t *testing.T) {
14997+
errs := New().Struct(&ValuerTypeWithPointerReceiver[SubTest]{})
14998+
AssertError(t, errs,
14999+
"ValuerTypeWithPointerReceiver[github.com/go-playground/validator/v10.SubTest].Data.Test",
15000+
"ValuerTypeWithPointerReceiver[github.com/go-playground/validator/v10.SubTest].Data.Test",
15001+
"Test", "Test", "required")
15002+
})
15003+
t.Run("pointer parent, pointer nested, pointer receiver (called)", func(t *testing.T) {
15004+
type Parent struct {
15005+
Nested *ValuerTypeWithPointerReceiver[SubTest] `validate:"required"`
15006+
}
15007+
15008+
errs := New().Struct(&Parent{})
15009+
AssertError(t, errs, "Parent.Nested", "Parent.Nested", "Nested", "Nested", "required")
15010+
15011+
errs = New().Struct(&Parent{
15012+
Nested: &ValuerTypeWithPointerReceiver[SubTest]{},
15013+
})
15014+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
15015+
15016+
errs = New().Struct(&Parent{
15017+
Nested: &ValuerTypeWithPointerReceiver[SubTest]{
15018+
Data: SubTest{
15019+
Test: "Test",
15020+
},
15021+
},
15022+
})
15023+
if errs != nil {
15024+
t.Fatalf("Expected no error, got: %v", errs)
15025+
}
15026+
})
15027+
t.Run("pointer parent, pointer nested, value receiver (called)", func(t *testing.T) {
15028+
type Parent struct {
15029+
Nested *ValuerTypeWithValueReceiver[SubTest] `validate:"required"`
15030+
}
15031+
15032+
errs := New().Struct(&Parent{})
15033+
AssertError(t, errs, "Parent.Nested", "Parent.Nested", "Nested", "Nested", "required")
15034+
15035+
errs = New().Struct(&Parent{
15036+
Nested: &ValuerTypeWithValueReceiver[SubTest]{},
15037+
})
15038+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
15039+
15040+
errs = New().Struct(&Parent{
15041+
Nested: &ValuerTypeWithValueReceiver[SubTest]{
15042+
Data: SubTest{
15043+
Test: "Test",
15044+
},
15045+
},
15046+
})
15047+
if errs != nil {
15048+
t.Fatalf("Expected no error, got: %v", errs)
15049+
}
15050+
})
15051+
t.Run("pointer parent, value nested, pointer receiver (not called)", func(t *testing.T) {
15052+
type Parent struct {
15053+
Nested ValuerTypeWithPointerReceiver[SubTest] `validate:"required"`
15054+
}
15055+
15056+
errs := New().Struct(&Parent{})
15057+
AssertError(t, errs, "Parent.Nested.Data.Test", "Parent.Nested.Data.Test", "Test", "Test", "required")
15058+
15059+
errs = New().Struct(&Parent{
15060+
Nested: ValuerTypeWithPointerReceiver[SubTest]{},
15061+
})
15062+
AssertError(t, errs, "Parent.Nested.Data.Test", "Parent.Nested.Data.Test", "Test", "Test", "required")
15063+
15064+
errs = New().Struct(&Parent{
15065+
Nested: ValuerTypeWithPointerReceiver[SubTest]{
15066+
Data: SubTest{
15067+
Test: "Test",
15068+
},
15069+
},
15070+
})
15071+
if errs != nil {
15072+
t.Fatalf("Expected no error, got: %v", errs)
15073+
}
15074+
})
15075+
t.Run("pointer parent, value nested, value receiver (called)", func(t *testing.T) {
15076+
type Parent struct {
15077+
Nested ValuerTypeWithValueReceiver[SubTest] `validate:"required"`
15078+
}
15079+
15080+
errs := New().Struct(&Parent{})
15081+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
15082+
15083+
errs = New().Struct(&Parent{
15084+
Nested: ValuerTypeWithValueReceiver[SubTest]{},
15085+
})
15086+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
15087+
15088+
errs = New().Struct(&Parent{
15089+
Nested: ValuerTypeWithValueReceiver[SubTest]{
15090+
Data: SubTest{
15091+
Test: "Test",
15092+
},
15093+
},
15094+
})
15095+
if errs != nil {
15096+
t.Fatalf("Expected no error, got: %v", errs)
15097+
}
15098+
})
15099+
t.Run("value parent, pointer nested, pointer receiver (called)", func(t *testing.T) {
15100+
type Parent struct {
15101+
Nested *ValuerTypeWithPointerReceiver[SubTest] `validate:"required"`
15102+
}
15103+
15104+
errs := New().Struct(Parent{})
15105+
AssertError(t, errs, "Parent.Nested", "Parent.Nested", "Nested", "Nested", "required")
15106+
15107+
errs = New().Struct(Parent{
15108+
Nested: &ValuerTypeWithPointerReceiver[SubTest]{},
15109+
})
15110+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
15111+
15112+
errs = New().Struct(Parent{
15113+
Nested: &ValuerTypeWithPointerReceiver[SubTest]{
15114+
Data: SubTest{
15115+
Test: "Test",
15116+
},
15117+
},
15118+
})
15119+
if errs != nil {
15120+
t.Fatalf("Expected no error, got: %v", errs)
15121+
}
15122+
})
15123+
t.Run("value parent, pointer nested, value receiver (called)", func(t *testing.T) {
15124+
type Parent struct {
15125+
Nested *ValuerTypeWithValueReceiver[SubTest] `validate:"required"`
15126+
}
15127+
15128+
errs := New().Struct(Parent{})
15129+
AssertError(t, errs, "Parent.Nested", "Parent.Nested", "Nested", "Nested", "required")
15130+
15131+
errs = New().Struct(Parent{
15132+
Nested: &ValuerTypeWithValueReceiver[SubTest]{},
15133+
})
15134+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
15135+
15136+
errs = New().Struct(Parent{
15137+
Nested: &ValuerTypeWithValueReceiver[SubTest]{
15138+
Data: SubTest{
15139+
Test: "Test",
15140+
},
15141+
},
15142+
})
15143+
if errs != nil {
15144+
t.Fatalf("Expected no error, got: %v", errs)
15145+
}
15146+
})
15147+
t.Run("value parent, value nested, pointer receiver (not called)", func(t *testing.T) {
15148+
type Parent struct {
15149+
Nested ValuerTypeWithPointerReceiver[SubTest] `validate:"required"`
15150+
}
15151+
15152+
errs := New().Struct(Parent{})
15153+
AssertError(t, errs, "Parent.Nested.Data.Test", "Parent.Nested.Data.Test", "Test", "Test", "required")
15154+
15155+
errs = New().Struct(Parent{
15156+
Nested: ValuerTypeWithPointerReceiver[SubTest]{},
15157+
})
15158+
AssertError(t, errs, "Parent.Nested.Data.Test", "Parent.Nested.Data.Test", "Test", "Test", "required")
15159+
15160+
errs = New().Struct(Parent{
15161+
Nested: ValuerTypeWithPointerReceiver[SubTest]{
15162+
Data: SubTest{
15163+
Test: "Test",
15164+
},
15165+
},
15166+
})
15167+
if errs != nil {
15168+
t.Fatalf("Expected no error, got: %v", errs)
15169+
}
15170+
})
15171+
t.Run("value parent, value nested, value receiver (called)", func(t *testing.T) {
15172+
type Parent struct {
15173+
Nested ValuerTypeWithValueReceiver[SubTest] `validate:"required"`
15174+
}
15175+
15176+
errs := New().Struct(Parent{})
15177+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
15178+
15179+
errs = New().Struct(Parent{
15180+
Nested: ValuerTypeWithValueReceiver[SubTest]{},
15181+
})
15182+
AssertError(t, errs, "Parent.Nested.Test", "Parent.Nested.Test", "Test", "Test", "required")
15183+
15184+
errs = New().Struct(Parent{
15185+
Nested: ValuerTypeWithValueReceiver[SubTest]{
15186+
Data: SubTest{
15187+
Test: "Test",
15188+
},
15189+
},
15190+
})
15191+
if errs != nil {
15192+
t.Fatalf("Expected no error, got: %v", errs)
15193+
}
15194+
})
15195+
}

0 commit comments

Comments
 (0)