Skip to content

Commit

Permalink
cr: introduce the configurable behaviour for the feature
Browse files Browse the repository at this point in the history
  • Loading branch information
nerzulk committed Feb 23, 2024
1 parent adbe200 commit 6292cff
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 122 deletions.
4 changes: 4 additions & 0 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr

fld = typ.Field(i)

if !v.privateFieldValidation && !fld.Anonymous && len(fld.PkgPath) > 0 {
continue
}

if rtag, ok := rules[fld.Name]; ok {
tag = rtag
} else {
Expand Down
9 changes: 9 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,12 @@ func WithRequiredStructEnabled() Option {
v.requiredStructEnabled = true
}
}

// WithPrivateFieldValidation activates validation for unexported fields
//
// It's experimental feature that partially uses unsafe package
func WithPrivateFieldValidation() Option {
return func(v *Validate) {
v.privateFieldValidation = true
}
}
29 changes: 15 additions & 14 deletions validator_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,20 +80,21 @@ type internalValidationFuncWrapper struct {

// Validate contains the validator settings and cache
type Validate struct {
tagName string
pool *sync.Pool
tagNameFunc TagNameFunc
structLevelFuncs map[reflect.Type]StructLevelFuncCtx
customFuncs map[reflect.Type]CustomTypeFunc
aliases map[string]string
validations map[string]internalValidationFuncWrapper
transTagFunc map[ut.Translator]map[string]TranslationFunc // map[<locale>]map[<tag>]TranslationFunc
rules map[reflect.Type]map[string]string
tagCache *tagCache
structCache *structCache
hasCustomFuncs bool
hasTagNameFunc bool
requiredStructEnabled bool
tagName string
pool *sync.Pool
tagNameFunc TagNameFunc
structLevelFuncs map[reflect.Type]StructLevelFuncCtx
customFuncs map[reflect.Type]CustomTypeFunc
aliases map[string]string
validations map[string]internalValidationFuncWrapper
transTagFunc map[ut.Translator]map[string]TranslationFunc // map[<locale>]map[<tag>]TranslationFunc
rules map[reflect.Type]map[string]string
tagCache *tagCache
structCache *structCache
hasCustomFuncs bool
hasTagNameFunc bool
requiredStructEnabled bool
privateFieldValidation bool
}

// New returns a new instance of 'validate' with sane defaults.
Expand Down
213 changes: 105 additions & 108 deletions validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -470,9 +470,8 @@ func TestAnonymous(t *testing.T) {

errs := err.(ValidationErrors)

Equal(t, len(errs), 2)
Equal(t, len(errs), 1)
AssertError(t, errs, "Test.AnonymousB.BEE", "Test.AnonymousB.B", "BEE", "B", "required")
AssertError(t, errs, "Test.anonymousC.c", "Test.anonymousC.c", "c", "c", "required")

fe := getError(errs, "Test.AnonymousB.BEE", "Test.AnonymousB.B")
NotEqual(t, fe, nil)
Expand All @@ -486,110 +485,7 @@ func TestAnonymous(t *testing.T) {
}

err = validate.Struct(s)
NotEqual(t, err, nil)
AssertError(t, err, "c", "c", "c", "c", "required")
}

func TestPrivateFieldsStruct(t *testing.T) {
type tc struct {
stct interface{}
errorNum int
}

tcs := []tc{
{
stct: &struct {
f1 int8 `validate:"required"`
f2 int16 `validate:"required"`
f3 int32 `validate:"required"`
f4 int64 `validate:"required"`
}{},
errorNum: 4,
},
{
stct: &struct {
f1 uint8 `validate:"required"`
f2 uint16 `validate:"required"`
f3 uint32 `validate:"required"`
f4 uint64 `validate:"required"`
}{},
errorNum: 4,
},
{
stct: &struct {
f1 complex64 `validate:"required"`
f2 complex128 `validate:"required"`
}{},
errorNum: 2,
},
{
stct: &struct {
f1 float32 `validate:"required"`
f2 float64 `validate:"required"`
}{},
errorNum: 2,
},
{
stct: struct {
f1 int8 `validate:"required"`
f2 int16 `validate:"required"`
f3 int32 `validate:"required"`
f4 int64 `validate:"required"`
}{},
errorNum: 4,
},
{
stct: struct {
f1 uint8 `validate:"required"`
f2 uint16 `validate:"required"`
f3 uint32 `validate:"required"`
f4 uint64 `validate:"required"`
}{},
errorNum: 4,
},
{
stct: struct {
f1 complex64 `validate:"required"`
f2 complex128 `validate:"required"`
}{},
errorNum: 2,
},
{
stct: struct {
f1 float32 `validate:"required"`
f2 float64 `validate:"required"`
}{},
errorNum: 2,
},
{
stct: struct {
f1 *int `validate:"required"`
f2 struct {
f3 int `validate:"required"`
}
}{},
errorNum: 2,
},
{
stct: &struct {
f1 *int `validate:"required"`
f2 struct {
f3 int `validate:"required"`
}
}{},
errorNum: 2,
},
}

validate := New()

for _, tc := range tcs {
err := validate.Struct(tc.stct)
NotEqual(t, err, nil)

errs := err.(ValidationErrors)
Equal(t, len(errs), tc.errorNum)
}
Equal(t, err, nil)
}

func TestAnonymousSameStructDifferentTags(t *testing.T) {
Expand Down Expand Up @@ -7553,8 +7449,7 @@ func TestUnexposedStruct(t *testing.T) {
Equal(t, s.unexposed.A, "")

errs := validate.Struct(s)
NotEqual(t, errs, nil)
AssertError(t, errs, "Test.unexposed.A", "Test.unexposed.A", "A", "A", "required")
Equal(t, errs, nil)
}

func TestBadParams(t *testing.T) {
Expand Down Expand Up @@ -13797,3 +13692,105 @@ func TestOmitNilAndRequired(t *testing.T) {
AssertError(t, err2, "OmitNil.Inner.Str", "OmitNil.Inner.Str", "Str", "Str", "required")
})
}

func TestPrivateFieldsStruct(t *testing.T) {
type tc struct {
stct interface{}
errorNum int
}

tcs := []tc{
{
stct: &struct {
f1 int8 `validate:"required"`
f2 int16 `validate:"required"`
f3 int32 `validate:"required"`
f4 int64 `validate:"required"`
}{},
errorNum: 4,
},
{
stct: &struct {
f1 uint8 `validate:"required"`
f2 uint16 `validate:"required"`
f3 uint32 `validate:"required"`
f4 uint64 `validate:"required"`
}{},
errorNum: 4,
},
{
stct: &struct {
f1 complex64 `validate:"required"`
f2 complex128 `validate:"required"`
}{},
errorNum: 2,
},
{
stct: &struct {
f1 float32 `validate:"required"`
f2 float64 `validate:"required"`
}{},
errorNum: 2,
},
{
stct: struct {
f1 int8 `validate:"required"`
f2 int16 `validate:"required"`
f3 int32 `validate:"required"`
f4 int64 `validate:"required"`
}{},
errorNum: 4,
},
{
stct: struct {
f1 uint8 `validate:"required"`
f2 uint16 `validate:"required"`
f3 uint32 `validate:"required"`
f4 uint64 `validate:"required"`
}{},
errorNum: 4,
},
{
stct: struct {
f1 complex64 `validate:"required"`
f2 complex128 `validate:"required"`
}{},
errorNum: 2,
},
{
stct: struct {
f1 float32 `validate:"required"`
f2 float64 `validate:"required"`
}{},
errorNum: 2,
},
{
stct: struct {
f1 *int `validate:"required"`
f2 struct {
f3 int `validate:"required"`
}
}{},
errorNum: 2,
},
{
stct: &struct {
f1 *int `validate:"required"`
f2 struct {
f3 int `validate:"required"`
}
}{},
errorNum: 2,
},
}

validate := New(WithPrivateFieldValidation())

for _, tc := range tcs {
err := validate.Struct(tc.stct)
NotEqual(t, err, nil)

errs := err.(ValidationErrors)
Equal(t, len(errs), tc.errorNum)
}
}

0 comments on commit 6292cff

Please sign in to comment.