Skip to content

Commit

Permalink
Feat: support validate struct without struct tag
Browse files Browse the repository at this point in the history
  • Loading branch information
leoliang committed Apr 21, 2022
1 parent 58d5778 commit adfb118
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 2 deletions.
92 changes: 92 additions & 0 deletions _examples/struct-map-rules-validation/main.go
@@ -0,0 +1,92 @@
package main

import (
"fmt"
"github.com/go-playground/validator/v10"
)

type Data struct {
Name string
Email string
Details *Details
}

type Details struct {
FamilyMembers *FamilyMembers
Salary string
}

type FamilyMembers struct {
FatherName string
MotherName string
}

type Data2 struct {
Name string
Age uint32
}

var validate = validator.New()

func main() {
validateStruct()
// output
// Key: 'Data2.Name' Error:Field validation for 'Name' failed on the 'min' tag
// Key: 'Data2.Age' Error:Field validation for 'Age' failed on the 'max' tag

validateStructNested()
// output
// Key: 'Data.Name' Error:Field validation for 'Name' failed on the 'max' tag
// Key: 'Data.Details.FamilyMembers' Error:Field validation for 'FamilyMembers' failed on the 'required' tag
}

func validateStruct() {
data := Data2{
Name: "leo",
Age: 1000,
}

rules := map[string]string{
"Name": "min=4,max=6",
"Age": "min=4,max=6",
}

validate.RegisterStructValidationMapRules(rules, Data2{})

err := validate.Struct(data)
fmt.Println(err)
fmt.Println()
}

func validateStructNested() {
data := Data{
Name: "11sdfddd111",
Email: "zytel3301@mail.com",
Details: &Details{
Salary: "1000",
},
}

rules1 := map[string]string{
"Name": "min=4,max=6",
"Email": "required,email",
"Details": "required",
}

rules2 := map[string]string{
"Salary": "number",
"FamilyMembers": "required",
}

rules3 := map[string]string{
"FatherName": "required,min=4,max=32",
"MotherName": "required,min=4,max=32",
}

validate.RegisterStructValidationMapRules(rules1, Data{})
validate.RegisterStructValidationMapRules(rules2, Details{})
validate.RegisterStructValidationMapRules(rules3, FamilyMembers{})
err := validate.Struct(data)

fmt.Println(err)
}
9 changes: 7 additions & 2 deletions cache.go
Expand Up @@ -114,12 +114,13 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr
cs = &cStruct{name: sName, fields: make([]*cField, 0), fn: v.structLevelFuncs[typ]}

numFields := current.NumField()
rules := v.rules[typ]

var ctag *cTag
var fld reflect.StructField
var tag string
var customName string

for i := 0; i < numFields; i++ {

fld = typ.Field(i)
Expand All @@ -128,7 +129,11 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr
continue
}

tag = fld.Tag.Get(v.tagName)
if rtag, ok := rules[fld.Name]; ok {
tag = rtag
} else {
tag = fld.Tag.Get(v.tagName)
}

if tag == skipValidationTag {
continue
Expand Down
28 changes: 28 additions & 0 deletions validator_instance.go
Expand Up @@ -84,6 +84,7 @@ type Validate struct {
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
}
Expand Down Expand Up @@ -271,6 +272,33 @@ func (v *Validate) RegisterStructValidationCtx(fn StructLevelFuncCtx, types ...i
}
}

// RegisterStructValidationMapRules registers validate map rules
//
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
func (v *Validate) RegisterStructValidationMapRules(rules map[string]string, types ...interface{}) {
if v.rules == nil {
v.rules = make(map[reflect.Type]map[string]string)
}

deepCopyRules := make(map[string]string)
for i, rule := range rules {
deepCopyRules[i] = rule
}

for _, t := range types {
typ := reflect.TypeOf(t)

if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}

if typ.Kind() != reflect.Struct {
continue
}
v.rules[typ] = deepCopyRules
}
}

// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types
//
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
Expand Down

0 comments on commit adfb118

Please sign in to comment.