Skip to content

Commit 305c50f

Browse files
Dean KarnDean Karn
authored andcommitted
Merge pull request #153 from bluesuncorp/v6
Merge latest changes into v7-development
2 parents a87651c + 6df82fd commit 305c50f

File tree

8 files changed

+1018
-217
lines changed

8 files changed

+1018
-217
lines changed

README.md

Lines changed: 92 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ It has the following **unique** features:
1212

1313
- Cross Field and Cross Struct validations.
1414
- Slice, Array and Map diving, which allows any or all levels of a multidimensional field to be validated.
15-
- Handles type interface by determining it's underlying type prior to validation.
15+
- Handles type interface by determining it's underlying type prior to validation.
16+
- Handles custom field types such as sql driver Valuer see [Valuer](https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29)
1617

1718
Installation
1819
------------
@@ -34,7 +35,9 @@ Usage and documentation
3435

3536
Please see http://godoc.org/gopkg.in/bluesuncorp/validator.v6 for detailed usage docs.
3637

37-
##### Example:
38+
##### Examples:
39+
40+
Struct & Field validation
3841
```go
3942
package main
4043

@@ -73,6 +76,12 @@ func main() {
7376

7477
validate = validator.New(config)
7578

79+
validateStruct()
80+
validateField()
81+
}
82+
83+
func validateStruct() {
84+
7685
address := &Address{
7786
Street: "Eavesdown Docks",
7887
Planet: "Persphone",
@@ -109,6 +118,71 @@ func main() {
109118

110119
// save user to database
111120
}
121+
122+
func validateField() {
123+
myEmail := "joeybloggs.gmail.com"
124+
125+
errs := validate.Field(myEmail, "required,email")
126+
127+
if errs != nil {
128+
fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "email" tag
129+
return
130+
}
131+
132+
// email ok, move on
133+
}
134+
```
135+
136+
Custom Field Type
137+
```go
138+
package main
139+
140+
import (
141+
"database/sql"
142+
"database/sql/driver"
143+
"fmt"
144+
"reflect"
145+
146+
"gopkg.in/bluesuncorp/validator.v6"
147+
)
148+
149+
// DbBackedUser User struct
150+
type DbBackedUser struct {
151+
Name sql.NullString `validate:"required"`
152+
Age sql.NullInt64 `validate:"required"`
153+
}
154+
155+
func main() {
156+
157+
config := validator.Config{
158+
TagName: "validate",
159+
ValidationFuncs: validator.BakedInValidators,
160+
}
161+
162+
validate := validator.New(config)
163+
164+
// register all sql.Null* types to use the ValidateValuer CustomTypeFunc
165+
validate.RegisterCustomTypeFunc(ValidateValuer, sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{})
166+
167+
x := DbBackedUser{Name: sql.NullString{String: "", Valid: true}, Age: sql.NullInt64{Int64: 0, Valid: false}}
168+
errs := validate.Struct(x)
169+
170+
if len(errs) > 0 {
171+
fmt.Printf("Errs:\n%+v\n", errs)
172+
}
173+
}
174+
175+
// ValidateValuer implements validator.CustomTypeFunc
176+
func ValidateValuer(field reflect.Value) interface{} {
177+
if valuer, ok := field.Interface().(driver.Valuer); ok {
178+
val, err := valuer.Value()
179+
if err == nil {
180+
return val
181+
}
182+
// handle the error how you want
183+
}
184+
return nil
185+
}
112186
```
113187

114188
Benchmarks
@@ -120,12 +194,22 @@ hurt parallel performance too much.
120194
```go
121195
$ go test -cpu=4 -bench=. -benchmem=true
122196
PASS
123-
BenchmarkField-4 5000000 314 ns/op 16 B/op 1 allocs/op
124-
BenchmarkFieldOrTag-4 500000 2425 ns/op 20 B/op 2 allocs/op
125-
BenchmarkStructSimple-4 500000 3117 ns/op 553 B/op 14 allocs/op
126-
BenchmarkStructSimpleParallel-4 1000000 1149 ns/op 553 B/op 14 allocs/op
127-
BenchmarkStructComplex-4 100000 19580 ns/op 3230 B/op 102 allocs/op
128-
BenchmarkStructComplexParallel-4 200000 6686 ns/op 3232 B/op 102 allocs/op
197+
BenchmarkFieldSuccess-4 5000000 318 ns/op 16 B/op 1 allocs/op
198+
BenchmarkFieldFailure-4 5000000 316 ns/op 16 B/op 1 allocs/op
199+
BenchmarkFieldCustomTypeSuccess-4 3000000 492 ns/op 32 B/op 2 allocs/op
200+
BenchmarkFieldCustomTypeFailure-4 2000000 843 ns/op 416 B/op 6 allocs/op
201+
BenchmarkFieldOrTagSuccess-4 500000 2384 ns/op 20 B/op 2 allocs/op
202+
BenchmarkFieldOrTagFailure-4 1000000 1295 ns/op 384 B/op 6 allocs/op
203+
BenchmarkStructSimpleSuccess-4 1000000 1175 ns/op 24 B/op 3 allocs/op
204+
BenchmarkStructSimpleFailure-4 1000000 1822 ns/op 529 B/op 11 allocs/op
205+
BenchmarkStructSimpleCustomTypeSuccess-4 1000000 1302 ns/op 56 B/op 5 allocs/op
206+
BenchmarkStructSimpleCustomTypeFailure-4 1000000 1847 ns/op 577 B/op 13 allocs/op
207+
BenchmarkStructSimpleSuccessParallel-4 5000000 339 ns/op 24 B/op 3 allocs/op
208+
BenchmarkStructSimpleFailureParallel-4 2000000 733 ns/op 529 B/op 11 allocs/op
209+
BenchmarkStructComplexSuccess-4 200000 7104 ns/op 368 B/op 30 allocs/op
210+
BenchmarkStructComplexFailure-4 100000 11996 ns/op 2861 B/op 72 allocs/op
211+
BenchmarkStructComplexSuccessParallel-4 1000000 2252 ns/op 368 B/op 30 allocs/op
212+
BenchmarkStructComplexFailureParallel-4 300000 4691 ns/op 2862 B/op 72 allocs/op
129213
```
130214

131215
How to Contribute

baked_in.go

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package validator
22

33
import (
44
"fmt"
5+
"net"
56
"net/url"
67
"reflect"
78
"strconv"
@@ -64,6 +65,35 @@ var BakedInValidators = map[string]Func{
6465
"latitude": isLatitude,
6566
"longitude": isLongitude,
6667
"ssn": isSSN,
68+
"ipv4": isIPv4,
69+
"ipv6": isIPv6,
70+
"ip": isIP,
71+
"mac": isMac,
72+
}
73+
74+
func isMac(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
75+
_, err := net.ParseMAC(field.String())
76+
return err == nil
77+
}
78+
79+
func isIPv4(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
80+
81+
ip := net.ParseIP(field.String())
82+
83+
return ip != nil && ip.To4() != nil
84+
}
85+
86+
func isIPv6(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
87+
ip := net.ParseIP(field.String())
88+
89+
return ip != nil && ip.To4() == nil
90+
}
91+
92+
func isIP(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
93+
94+
ip := net.ParseIP(field.String())
95+
96+
return ip != nil
6797
}
6898

6999
func isSSN(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
@@ -403,10 +433,8 @@ func isAlpha(topStruct reflect.Value, currentStruct reflect.Value, field reflect
403433
func hasValue(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
404434

405435
switch fieldKind {
406-
407-
case reflect.Slice, reflect.Map, reflect.Array:
408-
return !field.IsNil() && int64(field.Len()) > 0
409-
436+
case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
437+
return !field.IsNil()
410438
default:
411439
return field.IsValid() && field.Interface() != reflect.Zero(fieldType).Interface()
412440
}

0 commit comments

Comments
 (0)