diff --git a/_examples/struct-map-rules-validation/main.go b/_examples/struct-map-rules-validation/main.go new file mode 100644 index 00000000..e9361175 --- /dev/null +++ b/_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) +} diff --git a/cache.go b/cache.go index 0d18d6ec..7b84c91f 100644 --- a/cache.go +++ b/cache.go @@ -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) @@ -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 diff --git a/validator_instance.go b/validator_instance.go index 973964fc..d8671825 100644 --- a/validator_instance.go +++ b/validator_instance.go @@ -84,6 +84,7 @@ type Validate struct { aliases map[string]string validations map[string]internalValidationFuncWrapper transTagFunc map[ut.Translator]map[string]TranslationFunc // map[]map[]TranslationFunc + rules map[reflect.Type]map[string]string tagCache *tagCache structCache *structCache } @@ -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