Skip to content

Commit 6c939e2

Browse files
Dean KarnDean Karn
authored andcommitted
Merge branch 'v1-development' into v1
2 parents b6f1c7f + 5131e5e commit 6c939e2

File tree

4 files changed

+227
-54
lines changed

4 files changed

+227
-54
lines changed

baked_in.go

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,14 @@ package validator
33
import (
44
"log"
55
"reflect"
6-
"regexp"
76
"strconv"
87
)
98

10-
var bakedInValidators = map[string]ValidationFunc{
9+
var BakedInValidators = map[string]ValidationFunc{
1110
"required": required,
1211
"len": length,
1312
"min": min,
1413
"max": max,
15-
"regex": regex,
1614
}
1715

1816
func required(field interface{}, param string) bool {
@@ -151,27 +149,6 @@ func max(field interface{}, param string) bool {
151149
}
152150
}
153151

154-
// regex is the builtin validation function that checks
155-
// whether the string variable matches a regular expression
156-
func regex(field interface{}, param string) bool {
157-
158-
s, ok := field.(string)
159-
if !ok {
160-
log.Fatalf("Bad field type %s\n", field)
161-
}
162-
163-
re, err := regexp.Compile(param)
164-
if err != nil {
165-
log.Fatalf("Bad regex param %s\n", param)
166-
}
167-
168-
if !re.MatchString(s) {
169-
return false
170-
}
171-
172-
return true
173-
}
174-
175152
// asInt retuns the parameter as a int64
176153
// or panics if it can't convert
177154
func asInt(param string) int64 {

doc.go

Lines changed: 185 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,186 @@
1-
// Package validator implements value validations
2-
//
3-
// The MIT License (MIT)
4-
//
5-
// Copyright (c) 2015 Dean Karn
6-
//
7-
// Permission is hereby granted, free of charge, to any person obtaining a copy
8-
// of this software and associated documentation files (the "Software"), to deal
9-
// in the Software without restriction, including without limitation the rights
10-
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11-
// copies of the Software, and to permit persons to whom the Software is
12-
// furnished to do so, subject to the following conditions:
13-
//
14-
// The above copyright notice and this permission notice shall be included in all
15-
// copies or substantial portions of the Software.
16-
//
17-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18-
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19-
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20-
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21-
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22-
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23-
// SOFTWARE.
1+
/*
2+
Package validator implements value validations for structs and individual fields based on tags.
3+
4+
Built In Validator
5+
6+
The package contains a built in Validator instance for use,
7+
but you may also create a new instance if needed.
8+
9+
// built in
10+
errs := validator.ValidateStruct(//your struct)
11+
errs := validator.ValidateFieldByTag(field, "omitempty,min=1,max=10")
12+
13+
// new
14+
newValidator = validator.New("struct tag name", validator.BakedInFunctions)
15+
16+
A simple example usage:
17+
18+
type UserDetail {
19+
Details string `validate:"-"`
20+
}
21+
22+
type User struct {
23+
Name string `validate:"required,max=60"`
24+
PreferedName string `validate:"omitempty,max=60"`
25+
Sub UserDetail
26+
}
27+
28+
user := &User {
29+
Name: "",
30+
}
31+
32+
// errs will contain a hierarchical list of errors
33+
// using the StructValidationErrors struct
34+
// or nil if no errors exist
35+
errs := validator.ValidateStruct(user)
36+
37+
// in this case 1 error Name is required
38+
errs.Struct will be "User"
39+
errs.StructErrors will be empty <-- fields that were structs
40+
errs.Errors will have 1 error of type FieldValidationError
41+
42+
Error Handling
43+
44+
The error can be used like so
45+
46+
fieldErr, _ := errs["Name"]
47+
fieldErr.Field // "Name"
48+
fieldErr.ErrorTag // "required"
49+
50+
Both StructValidationErrors and FieldValidationError implement the Error interface but it's
51+
intended use is for development + debugging, not a production error message.
52+
53+
fieldErr.Error() // Field validation for "Name" failed on the "required" tag
54+
errs.Error()
55+
// Struct: User
56+
// Field validation for "Name" failed on the "required" tag
57+
58+
Why not a better error message? because this library intends for you to handle your own error messages
59+
60+
Why should I handle my own errors? Many reasons, for me building and internationalized application
61+
I needed to know the field and what validation failed so that I could provide an error in the users specific language.
62+
63+
if fieldErr.Field == "Name" {
64+
switch fieldErr.ErrorTag
65+
case "required":
66+
return "Translated string based on field + error"
67+
default:
68+
return "Translated string based on field"
69+
}
70+
71+
The hierarchical structure is hard to work with sometimes.. Agreed Flatten function to the rescue!
72+
Flatten will return a map of FieldValidationError's but the field name will be namespaced.
73+
74+
// if UserDetail Details field failed validation
75+
Field will be "Sub.Details"
76+
77+
// for Name
78+
Field will be "Name"
79+
80+
Custom Functions
81+
82+
Custom functions can be added
83+
84+
//Structure
85+
func customFunc(field interface{}, param string) bool {
86+
87+
if whatever {
88+
return false
89+
}
90+
91+
return true
92+
}
93+
94+
validator.AddFunction("custom tag name", customFunc)
95+
// NOTE: using the same tag name as an existing function
96+
// will overwrite the existing one
97+
98+
Custom Tag Name
99+
100+
A custom tag name can be set to avoid conficts, or just have a shorter name
101+
102+
validator.SetTag("valid")
103+
104+
Multiple Validators
105+
106+
Multiple validators on a field will process in the order defined
107+
108+
type Test struct {
109+
Field `validate:"max=10,min=1"`
110+
}
111+
112+
// max will be checked then min
113+
114+
Bad Validator definitions are not handled by the library
115+
116+
type Test struct {
117+
Field `validate:"min=10,max=0"`
118+
}
119+
120+
// this definition of min max will never validate
121+
122+
Baked In Validators
123+
124+
Here is a list of the current built in validators:
125+
126+
-
127+
Tells the validation to skip this struct field; this is particularily
128+
handy in ignoring embedded structs from being validated. (Usage: -)
129+
130+
omitempty
131+
Allows conitional validation, for example if a field is not set with
132+
a value (Determined by the required validator) then other validation
133+
such as min or max won't run, but if a value is set validation will run.
134+
(Usage: omitempty)
135+
len
136+
For numbers, max will ensure that the value is
137+
equal to the parameter given. For strings, it checks that
138+
the string length is exactly that number of characters. For slices,
139+
arrays, and maps, validates the number of items. (Usage: len=10)
140+
max
141+
For numbers, max will ensure that the value is
142+
less than or equal to the parameter given. For strings, it checks
143+
that the string length is at most that number of characters. For
144+
slices, arrays, and maps, validates the number of items. (Usage: max=10)
145+
min
146+
For numbers, min will ensure that the value is
147+
greater or equal to the parameter given. For strings, it checks that
148+
the string length is at least that number of characters. For slices,
149+
arrays, and maps, validates the number of items. (Usage: min=10)
150+
151+
required
152+
This validates that the value is not the data types default value.
153+
For numbers ensures value is not zero. For strings ensures value is
154+
not "". For slices, arrays, and maps, ensures the length is not zero.
155+
(Usage: required)
156+
157+
Validator notes:
158+
159+
regex
160+
a regex validator won't be added because commas and = signs can be part of
161+
a regex which conflict with the validation definitions, although workarounds
162+
can be made, they take away from using pure regex's. Furthermore it's quick
163+
and dirty but the regex's become harder to maintain and are not reusable, so
164+
it's as much as a programming philosiphy as anything.
165+
166+
In place of this new validator functions should be created; a regex can be
167+
used within the validator function and even be precompiled for better efficiency.
168+
169+
And the best reason, you can sumit a pull request and we can keep on adding to the
170+
validation library of this package!
171+
172+
Panics
173+
174+
This package panics when bad input is provided, this is by design, bad code like that should not make it to production.
175+
176+
type Test struct {
177+
TestField string `validate:"nonexistantfunction=1"`
178+
}
179+
180+
t := &Test{
181+
TestField: "Test"
182+
}
183+
184+
validator.ValidateStruct(t) // this will panic
185+
*/
24186
package validator

validator.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ type Validator struct {
108108

109109
// var bakedInValidators = map[string]ValidationFunc{}
110110

111-
var internalValidator = NewValidator(defaultTagName, bakedInValidators)
111+
var internalValidator = NewValidator(defaultTagName, BakedInValidators)
112112

113113
// NewValidator creates a new Validator instance
114114
// NOTE: it is not necessary to create a new validator as the internal one will do in 99.9% of cases, but the option is there.
@@ -178,7 +178,7 @@ func (v *Validator) ValidateStruct(s interface{}) *StructValidationErrors {
178178
return v.ValidateStruct(structValue.Elem().Interface())
179179
}
180180

181-
if structValue.Kind() != reflect.Struct {
181+
if structValue.Kind() != reflect.Struct && structValue.Kind() != reflect.Interface {
182182
panic("interface passed for validation is not a struct")
183183
}
184184

@@ -200,13 +200,13 @@ func (v *Validator) ValidateStruct(s interface{}) *StructValidationErrors {
200200
}
201201

202202
// if no validation and not a struct (which may containt fields for validation)
203-
if tag == "" && valueField.Kind() != reflect.Struct {
203+
if tag == "" && valueField.Kind() != reflect.Struct && valueField.Kind() != reflect.Interface {
204204
continue
205205
}
206206

207207
switch valueField.Kind() {
208208

209-
case reflect.Struct:
209+
case reflect.Struct, reflect.Interface:
210210

211211
if !unicode.IsUpper(rune(typeField.Name[0])) {
212212
continue
@@ -283,10 +283,12 @@ func (v *Validator) validateFieldByNameAndTag(f interface{}, name string, tag st
283283
panic("Invalid field passed to ValidateFieldWithTag")
284284
}
285285

286+
// TODO: validate commas in regex's
286287
valTags := strings.Split(tag, ",")
287288

288289
for _, valTag := range valTags {
289290

291+
// TODO: validate = in regex's
290292
vals := strings.Split(valTag, "=")
291293
key := strings.Trim(vals[0], " ")
292294

@@ -299,8 +301,8 @@ func (v *Validator) validateFieldByNameAndTag(f interface{}, name string, tag st
299301
continue
300302
}
301303

302-
valFunc := v.validationFuncs[key]
303-
if valFunc == nil {
304+
valFunc, ok := v.validationFuncs[key]
305+
if !ok {
304306
panic(fmt.Sprintf("Undefined validation function on field %s", name))
305307
}
306308

0 commit comments

Comments
 (0)