Skip to content

Commit 80db7ad

Browse files
committed
feat: omit anonymous embedded struct
1 parent 84254ae commit 80db7ad

File tree

4 files changed

+39
-19
lines changed

4 files changed

+39
-19
lines changed

options.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,10 @@ func WithRequiredStructEnabled() Option {
1414
v.requiredStructEnabled = true
1515
}
1616
}
17+
18+
// WithRequiredStructEnabled enables omitting the name of embedded anonymous structs from the namespace.
19+
func WithOmitAnonymousName() Option {
20+
return func(v *Validate) {
21+
v.omitAnonymousName = true
22+
}
23+
}

validator.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ type validate struct {
3131

3232
// parent and current will be the same the first run of validateStruct
3333
func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, current reflect.Value, typ reflect.Type, ns []byte, structNs []byte, ct *cTag) {
34-
3534
cs, ok := v.v.structCache.Get(typ)
3635
if !ok {
3736
cs = v.v.extractStructCache(current, typ.Name())
@@ -193,7 +192,7 @@ OUTER:
193192
// Var - doesn't make much sense to do it that way, should call 'Struct', but no harm...
194193
// VarWithField - this allows for validating against each field within the struct against a specific value
195194
// pretty handy in certain situations
196-
if len(cf.name) > 0 {
195+
if len(cf.name) > 0 && !(v.v.omitAnonymousName && parent.Type().Field(cf.idx).Anonymous) {
197196
ns = append(append(ns, cf.altName...), '.')
198197
structNs = append(append(structNs, cf.name...), '.')
199198
}
@@ -482,5 +481,4 @@ OUTER:
482481
ct = ct.next
483482
}
484483
}
485-
486484
}

validator_instance.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ type Validate struct {
9494
hasCustomFuncs bool
9595
hasTagNameFunc bool
9696
requiredStructEnabled bool
97+
omitAnonymousName bool
9798
}
9899

99100
// New returns a new instance of 'validate' with sane defaults.
@@ -102,7 +103,6 @@ type Validate struct {
102103
// in essence only parsing your validation tags once per struct type.
103104
// Using multiple instances neglects the benefit of caching.
104105
func New(options ...Option) *Validate {
105-
106106
tc := new(tagCache)
107107
tc.m.Store(make(map[string]*cTag))
108108

@@ -124,7 +124,6 @@ func New(options ...Option) *Validate {
124124

125125
// must copy validators for separate validations to be used in each instance
126126
for k, val := range bakedInValidators {
127-
128127
switch k {
129128
// these require that even if the value is nil that the validation should run, omitempty still overrides this behaviour
130129
case requiredIfTag, requiredUnlessTag, requiredWithTag, requiredWithAllTag, requiredWithoutTag, requiredWithoutAllTag,
@@ -254,7 +253,6 @@ func (v *Validate) registerValidation(tag string, fn FuncCtx, bakedIn bool, nilC
254253
//
255254
// NOTE: this function is not thread-safe it is intended that these all be registered prior to any validation
256255
func (v *Validate) RegisterAlias(alias, tags string) {
257-
258256
_, ok := restrictedTags[alias]
259257

260258
if ok || strings.ContainsAny(alias, restrictedTagChars) {
@@ -278,7 +276,6 @@ func (v *Validate) RegisterStructValidation(fn StructLevelFunc, types ...interfa
278276
// NOTE:
279277
// - this method is not thread-safe it is intended that these all be registered prior to any validation
280278
func (v *Validate) RegisterStructValidationCtx(fn StructLevelFuncCtx, types ...interface{}) {
281-
282279
if v.structLevelFuncs == nil {
283280
v.structLevelFuncs = make(map[reflect.Type]StructLevelFuncCtx)
284281
}
@@ -325,7 +322,6 @@ func (v *Validate) RegisterStructValidationMapRules(rules map[string]string, typ
325322
//
326323
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
327324
func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{}) {
328-
329325
if v.customFuncs == nil {
330326
v.customFuncs = make(map[reflect.Type]CustomTypeFunc)
331327
}
@@ -339,7 +335,6 @@ func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{
339335

340336
// RegisterTranslation registers translations against the provided tag.
341337
func (v *Validate) RegisterTranslation(tag string, trans ut.Translator, registerFn RegisterTranslationsFunc, translationFn TranslationFunc) (err error) {
342-
343338
if v.transTagFunc == nil {
344339
v.transTagFunc = make(map[ut.Translator]map[string]TranslationFunc)
345340
}
@@ -373,7 +368,6 @@ func (v *Validate) Struct(s interface{}) error {
373368
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
374369
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
375370
func (v *Validate) StructCtx(ctx context.Context, s interface{}) (err error) {
376-
377371
val := reflect.ValueOf(s)
378372
top := val
379373

validator_test.go

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -746,11 +746,9 @@ func TestStructPartial(t *testing.T) {
746746

747747
SubSlice: []*SubTest{
748748
{
749-
750749
Test: "Required",
751750
},
752751
{
753-
754752
Test: "Required",
755753
},
756754
},
@@ -4002,7 +4000,6 @@ func TestUUID5Validation(t *testing.T) {
40024000
param string
40034001
expected bool
40044002
}{
4005-
40064003
{"", false},
40074004
{"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false},
40084005
{"9c858901-8a57-4791-81fe-4c455b099bc9", false},
@@ -4190,7 +4187,6 @@ func TestUUID5RFC4122Validation(t *testing.T) {
41904187
param string
41914188
expected bool
41924189
}{
4193-
41944190
{"", false},
41954191
{"xxxa987Fbc9-4bed-3078-cf07-9141ba07c9f3", false},
41964192
{"9c858901-8a57-4791-81Fe-4c455b099bc9", false},
@@ -8793,6 +8789,7 @@ func TestNumeric(t *testing.T) {
87938789
errs = validate.Var(i, "numeric")
87948790
Equal(t, errs, nil)
87958791
}
8792+
87968793
func TestBoolean(t *testing.T) {
87978794
validate := New()
87988795

@@ -9518,11 +9515,9 @@ func TestStructFiltered(t *testing.T) {
95189515

95199516
SubSlice: []*SubTest{
95209517
{
9521-
95229518
Test: "Required",
95239519
},
95249520
{
9525-
95269521
Test: "Required",
95279522
},
95289523
},
@@ -12909,7 +12904,6 @@ func TestSemverFormatValidation(t *testing.T) {
1290912904
}
1291012905

1291112906
func TestCveFormatValidation(t *testing.T) {
12912-
1291312907
tests := []struct {
1291412908
value string `validate:"cve"`
1291512909
tag string
@@ -13106,7 +13100,6 @@ func TestPostCodeByIso3166Alpha2Field_InvalidKind(t *testing.T) {
1310613100
}
1310713101

1310813102
func TestValidate_ValidateMapCtx(t *testing.T) {
13109-
1311013103
type args struct {
1311113104
data map[string]interface{}
1311213105
rules map[string]interface{}
@@ -13588,7 +13581,7 @@ func TestNestedStructValidation(t *testing.T) {
1358813581
},
1358913582
}
1359013583

13591-
var evaluateTest = func(tt test, errs error) {
13584+
evaluateTest := func(tt test, errs error) {
1359213585
if tt.err != (testErr{}) && errs != nil {
1359313586
Equal(t, len(errs.(ValidationErrors)), 1)
1359413587

@@ -13626,6 +13619,34 @@ func TestNestedStructValidation(t *testing.T) {
1362613619
}
1362713620
}
1362813621

13622+
func TestHideEmbeddedStructName(t *testing.T) {
13623+
validate := New(WithOmitAnonymousName())
13624+
13625+
type Inner struct {
13626+
Test string `validate:"required"`
13627+
}
13628+
13629+
t.Run("anonymous", func(t *testing.T) {
13630+
type Anonymous struct {
13631+
Inner
13632+
}
13633+
13634+
err := validate.Struct(Anonymous{})
13635+
NotEqual(t, err, nil)
13636+
AssertError(t, err.(ValidationErrors), "Anonymous.Test", "Anonymous.Test", "Test", "Test", "required")
13637+
})
13638+
13639+
t.Run("named", func(t *testing.T) {
13640+
type Named struct {
13641+
Inner Inner
13642+
}
13643+
13644+
err := validate.Struct(Named{})
13645+
NotEqual(t, err, nil)
13646+
AssertError(t, err.(ValidationErrors), "Named.Inner.Test", "Named.Inner.Test", "Test", "Test", "required")
13647+
})
13648+
}
13649+
1362913650
func TestTimeRequired(t *testing.T) {
1363013651
validate := New()
1363113652
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {

0 commit comments

Comments
 (0)