Skip to content

Commit 3bece51

Browse files
committed
Merge branch 'v5-development' of https://gopkg.in/bluesuncorp/validator.v5 into v5-development
2 parents 0fa8dd2 + 988489d commit 3bece51

File tree

11 files changed

+3026
-258
lines changed

11 files changed

+3026
-258
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,6 @@ _testmain.go
2323
*.test
2424
*.prof
2525
*.test
26-
*.out
26+
*.out
27+
cover.html
28+
README.html

.travis.yml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,14 @@ notificaitons:
77
on_failure: always
88

99
go:
10-
- 1.2
1110
- 1.3
1211
- 1.4
13-
- tip
12+
- tip
13+
14+
script:
15+
- go get golang.org/x/tools/cmd/cover
16+
- go get github.com/mattn/goveralls
17+
- go test -v -covermode=count -coverprofile=cover.out
18+
19+
after_success:
20+
- goveralls -coverprofile=cover.out -service=travis-ci -repotoken I6M8FiXZzErImgwMotJ7fwFlHOX8Hqdq1

README.md

Lines changed: 116 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
11
Package validator
22
================
3+
4+
[![Join the chat at https://gitter.im/bluesuncorp/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/bluesuncorp/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
35
[![Build Status](https://travis-ci.org/bluesuncorp/validator.svg?branch=v5.1)](https://travis-ci.org/bluesuncorp/validator)
6+
[![Coverage Status](https://coveralls.io/repos/bluesuncorp/validator/badge.svg?branch=v5)](https://coveralls.io/r/bluesuncorp/validator?branch=v5)
47
[![GoDoc](https://godoc.org/gopkg.in/bluesuncorp/validator.v5?status.svg)](https://godoc.org/gopkg.in/bluesuncorp/validator.v5)
58

69
Package validator implements value validations for structs and individual fields based on tags.
7-
It is also capable of Cross Field and Cross Struct validations.
10+
11+
It has the following **unique** features:
12+
13+
- Cross Field and Cross Struct validations.
14+
- 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.
816

917
Installation
10-
============
18+
------------
1119

1220
Use go get.
1321

14-
go get -u gopkg.in/bluesuncorp/validator.v5
22+
go get gopkg.in/bluesuncorp/validator.v5
1523

1624
or to update
1725

@@ -22,12 +30,114 @@ Then import the validator package into your own code.
2230
import "gopkg.in/bluesuncorp/validator.v5"
2331

2432
Usage and documentation
25-
=======================
33+
------
2634

2735
Please see http://godoc.org/gopkg.in/bluesuncorp/validator.v5 for detailed usage docs.
2836

37+
##### Example:
38+
```go
39+
package main
40+
41+
import (
42+
"fmt"
43+
44+
"gopkg.in/bluesuncorp/validator.v5"
45+
)
46+
47+
// User contains user information
48+
type User struct {
49+
FirstName string `validate:"required"`
50+
LastName string `validate:"required"`
51+
Age uint8 `validate:"gte=0,lte=130"`
52+
Email string `validate:"required,email"`
53+
FavouriteColor string `validate:"hexcolor|rgb|rgba"`
54+
Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
55+
}
56+
57+
// Address houses a users address information
58+
type Address struct {
59+
Street string `validate:"required"`
60+
City string `validate:"required"`
61+
Planet string `validate:"required"`
62+
Phone string `validate:"required"`
63+
}
64+
65+
var validate *validator.Validate
66+
67+
func main() {
68+
69+
validate = validator.New("validate", validator.BakedInValidators)
70+
71+
address := &Address{
72+
Street: "Eavesdown Docks",
73+
Planet: "Persphone",
74+
Phone: "none",
75+
}
76+
77+
user := &User{
78+
FirstName: "Badger",
79+
LastName: "Smith",
80+
Age: 135,
81+
82+
FavouriteColor: "#000",
83+
Addresses: []*Address{address},
84+
}
85+
86+
// returns nil or *StructErrors
87+
errs := validate.Struct(user)
88+
89+
if errs != nil {
90+
91+
// err will be of type *FieldError
92+
err := errs.Errors["Age"]
93+
fmt.Println(err.Error()) // output: Field validation for "Age" failed on the "lte" tag
94+
fmt.Println(err.Field) // output: Age
95+
fmt.Println(err.Tag) // output: lte
96+
fmt.Println(err.Kind) // output: uint8
97+
fmt.Println(err.Type) // output: uint8
98+
fmt.Println(err.Param) // output: 130
99+
fmt.Println(err.Value) // output: 135
100+
101+
// or if you prefer you can use the Flatten function
102+
// NOTE: I find this usefull when using a more hard static approach of checking field errors.
103+
// The above, is best for passing to some generic code to say parse the errors. i.e. I pass errs
104+
// to a routine which loops through the errors, creates and translates the error message into the
105+
// users locale and returns a map of map[string]string // field and error which I then use
106+
// within the HTML rendering.
107+
108+
flat := errs.Flatten()
109+
fmt.Println(flat) // output: map[Age:Field validation for "Age" failed on the "lte" tag Addresses[0].Address.City:Field validation for "City" failed on the "required" tag]
110+
err = flat["Addresses[0].Address.City"]
111+
fmt.Println(err.Field) // output: City
112+
fmt.Println(err.Tag) // output: required
113+
fmt.Println(err.Kind) // output: string
114+
fmt.Println(err.Type) // output: string
115+
fmt.Println(err.Param) // output:
116+
fmt.Println(err.Value) // output:
117+
118+
// from here you can create your own error messages in whatever language you wish
119+
return
120+
}
121+
122+
// save user to database
123+
}
124+
```
125+
126+
Benchmarks
127+
------
128+
###### Run on MacBook Pro (Retina, 15-inch, Late 2013) 2.6 GHz Intel Core i7 16 GB 1600 MHz DDR3
129+
```go
130+
$ go test -cpu=4 -bench=. -benchmem=true
131+
PASS
132+
BenchmarkValidateField-4 3000000 429 ns/op 192 B/op 2 allocs/op
133+
BenchmarkValidateStructSimple-4 500000 2877 ns/op 657 B/op 10 allocs/op
134+
BenchmarkTemplateParallelSimple-4 500000 3097 ns/op 657 B/op 10 allocs/op
135+
BenchmarkValidateStructLarge-4 100000 15228 ns/op 4350 B/op 62 allocs/op
136+
BenchmarkTemplateParallelLarge-4 100000 14257 ns/op 4354 B/op 62 allocs/op
137+
```
138+
29139
How to Contribute
30-
=================
140+
------
31141

32142
There will always be a development branch for each version i.e. `v1-development`. In order to contribute,
33143
please make your pull requests against those branches.
@@ -40,5 +150,5 @@ I strongly encourage everyone whom creates a custom validation function to contr
40150
help make this package even better.
41151

42152
License
43-
=======
153+
------
44154
Distributed under MIT License, please see license file in code for more details.

baked_in.go

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,141 @@ var BakedInValidators = map[string]Func{
5050
"excludes": excludes,
5151
"excludesall": excludesAll,
5252
"excludesrune": excludesRune,
53+
"isbn": isISBN,
54+
"isbn10": isISBN10,
55+
"isbn13": isISBN13,
56+
"uuid": isUUID,
57+
"uuid3": isUUID3,
58+
"uuid4": isUUID4,
59+
"uuid5": isUUID5,
60+
"ascii": isASCII,
61+
"printascii": isPrintableASCII,
62+
"multibyte": hasMultiByteCharacter,
63+
"datauri": isDataURI,
64+
"latitude": isLatitude,
65+
"longitude": isLongitude,
66+
"ssn": isSSN,
67+
}
68+
69+
func isSSN(top interface{}, current interface{}, field interface{}, param string) bool {
70+
71+
if len(field.(string)) != 11 {
72+
return false
73+
}
74+
75+
return matchesRegex(sSNRegex, field)
76+
}
77+
78+
func isLongitude(top interface{}, current interface{}, field interface{}, param string) bool {
79+
return matchesRegex(longitudeRegex, field)
80+
}
81+
82+
func isLatitude(top interface{}, current interface{}, field interface{}, param string) bool {
83+
return matchesRegex(latitudeRegex, field)
84+
}
85+
86+
func isDataURI(top interface{}, current interface{}, field interface{}, param string) bool {
87+
88+
uri := strings.SplitN(field.(string), ",", 2)
89+
90+
if len(uri) != 2 {
91+
return false
92+
}
93+
94+
if !matchesRegex(dataURIRegex, uri[0]) {
95+
return false
96+
}
97+
98+
return isBase64(top, current, uri[1], param)
99+
}
100+
101+
func hasMultiByteCharacter(top interface{}, current interface{}, field interface{}, param string) bool {
102+
103+
if len(field.(string)) == 0 {
104+
return true
105+
}
106+
107+
return matchesRegex(multibyteRegex, field)
108+
}
109+
110+
func isPrintableASCII(top interface{}, current interface{}, field interface{}, param string) bool {
111+
return matchesRegex(printableASCIIRegex, field)
112+
}
113+
114+
func isASCII(top interface{}, current interface{}, field interface{}, param string) bool {
115+
return matchesRegex(aSCIIRegex, field)
116+
}
117+
118+
func isUUID5(top interface{}, current interface{}, field interface{}, param string) bool {
119+
return matchesRegex(uUID5Regex, field)
120+
}
121+
122+
func isUUID4(top interface{}, current interface{}, field interface{}, param string) bool {
123+
return matchesRegex(uUID4Regex, field)
124+
}
125+
126+
func isUUID3(top interface{}, current interface{}, field interface{}, param string) bool {
127+
return matchesRegex(uUID3Regex, field)
128+
}
129+
130+
func isUUID(top interface{}, current interface{}, field interface{}, param string) bool {
131+
return matchesRegex(uUIDRegex, field)
132+
}
133+
134+
func isISBN(top interface{}, current interface{}, field interface{}, param string) bool {
135+
return isISBN10(top, current, field, param) || isISBN13(top, current, field, param)
136+
}
137+
138+
func isISBN13(top interface{}, current interface{}, field interface{}, param string) bool {
139+
140+
s := strings.Replace(strings.Replace(field.(string), "-", "", 4), " ", "", 4)
141+
142+
if !matchesRegex(iSBN13Regex, s) {
143+
return false
144+
}
145+
146+
var checksum int32
147+
var i int32
148+
149+
factor := []int32{1, 3}
150+
151+
for i = 0; i < 12; i++ {
152+
checksum += factor[i%2] * int32(s[i]-'0')
153+
}
154+
155+
if (int32(s[12]-'0'))-((10-(checksum%10))%10) == 0 {
156+
return true
157+
}
158+
159+
return false
160+
}
161+
162+
func isISBN10(top interface{}, current interface{}, field interface{}, param string) bool {
163+
164+
s := strings.Replace(strings.Replace(field.(string), "-", "", 3), " ", "", 3)
165+
166+
if !matchesRegex(iSBN10Regex, s) {
167+
return false
168+
}
169+
170+
var checksum int32
171+
var i int32
172+
173+
for i = 0; i < 9; i++ {
174+
checksum += (i + 1) * int32(s[i]-'0')
175+
}
176+
177+
if s[9] == 'X' {
178+
checksum += 10 * 10
179+
} else {
180+
checksum += 10 * int32(s[9]-'0')
181+
}
182+
183+
if checksum%11 == 0 {
184+
return true
185+
}
186+
187+
return false
53188
}
54189

55190
func excludesRune(top interface{}, current interface{}, field interface{}, param string) bool {

0 commit comments

Comments
 (0)