add isbn, isbn10 and isbn validators + tests + documentation

pull/75/head
joeybloggs 10 years ago
parent 05e0fe1f85
commit 20d9b7909f
  1. 59
      baked_in.go
  2. 12
      doc.go
  3. 4
      regexes.go
  4. 98
      validator_test.go

@ -50,6 +50,65 @@ var BakedInValidators = map[string]Func{
"excludes": excludes,
"excludesall": excludesAll,
"excludesrune": excludesRune,
"isbn": isISBN,
"isbn10": isISBN10,
"isbn13": isISBN13,
}
func isISBN(top interface{}, current interface{}, field interface{}, param string) bool {
return isISBN10(top, current, field, param) || isISBN13(top, current, field, param)
}
func isISBN13(top interface{}, current interface{}, field interface{}, param string) bool {
s := strings.Replace(strings.Replace(field.(string), "-", "", 4), " ", "", 4)
if !matchesRegex(iSBN13Regex, s) {
return false
}
var checksum int32
var i int32
factor := []int32{1, 3}
for i = 0; i < 12; i++ {
checksum += factor[i%2] * int32(s[i]-'0')
}
if (int32(s[12]-'0'))-((10-(checksum%10))%10) == 0 {
return true
}
return false
}
func isISBN10(top interface{}, current interface{}, field interface{}, param string) bool {
s := strings.Replace(strings.Replace(field.(string), "-", "", 3), " ", "", 3)
if !matchesRegex(iSBN10Regex, s) {
return false
}
var checksum int32
var i int32
for i = 0; i < 9; i++ {
checksum += (i + 1) * int32(s[i]-'0')
}
if s[9] == 'X' {
checksum += 10 * 10
} else {
checksum += 10 * int32(s[9]-'0')
}
if checksum%11 == 0 {
return true
}
return false
}
func excludesRune(top interface{}, current interface{}, field interface{}, param string) bool {

@ -362,6 +362,18 @@ Here is a list of the current built in validators:
This validates that a string value does not contain the supplied rune value.
(Usage: excludesrune=@)
isbn
This validates that a string value contains a valid isbn10 or isbn13 value.
(Usage: isbn)
isbn10
This validates that a string value contains a valid isbn10 value.
(Usage: isbn10)
isbn13
This validates that a string value contains a valid isbn13 value.
(Usage: isbn13)
Validator notes:
regex

@ -15,6 +15,8 @@ const (
hslaRegexString = "^hsla\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$"
emailRegexString = "^(?:(?:(?:(?:[a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(?:\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|(?:(?:\\x22)(?:(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(?:\\x20|\\x09)+)?(?:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:\\(?:[\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(\\x20|\\x09)+)?(?:\\x22)))@(?:(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$"
base64RegexString = "(?:^(?:[A-Za-z0-9+\\/]{4}\\n?)*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=)$)"
iSBN10RegexString = "^(?:[0-9]{9}X|[0-9]{10})$"
iSBN13RegexString = "^(?:(?:97(?:8|9))[0-9]{10})$"
)
var (
@ -30,6 +32,8 @@ var (
hslaRegex = regexp.MustCompile(hslaRegexString)
emailRegex = regexp.MustCompile(emailRegexString)
base64Regex = regexp.MustCompile(base64RegexString)
iSBN10Regex = regexp.MustCompile(iSBN10RegexString)
iSBN13Regex = regexp.MustCompile(iSBN13RegexString)
)
func matchesRegex(regex *regexp.Regexp, field interface{}) bool {

@ -226,6 +226,104 @@ func AssertMapFieldError(t *testing.T, s map[string]*FieldError, field string, e
EqualSkip(t, 2, val.Tag, expectedTag)
}
func TestISBNValidation(t *testing.T) {
tests := []struct {
param string
expected bool
}{
{"", false},
{"foo", false},
{"3836221195", true},
{"1-61729-085-8", true},
{"3 423 21412 0", true},
{"3 401 01319 X", true},
{"9784873113685", true},
{"978-4-87311-368-5", true},
{"978 3401013190", true},
{"978-3-8362-2119-1", true},
}
for i, test := range tests {
err := validate.Field(test.param, "isbn")
if test.expected == true {
if !IsEqual(t, err, nil) {
t.Fatalf("Index: %d ISBN failed Error: %s", i, err)
}
} else {
if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "isbn") {
t.Fatalf("Index: %d ISBN failed Error: %s", i, err)
}
}
}
}
func TestISBN13Validation(t *testing.T) {
tests := []struct {
param string
expected bool
}{
{"", false},
{"foo", false},
{"3-8362-2119-5", false},
{"01234567890ab", false},
{"978 3 8362 2119 0", false},
{"9784873113685", true},
{"978-4-87311-368-5", true},
{"978 3401013190", true},
{"978-3-8362-2119-1", true},
}
for i, test := range tests {
err := validate.Field(test.param, "isbn13")
if test.expected == true {
if !IsEqual(t, err, nil) {
t.Fatalf("Index: %d ISBN13 failed Error: %s", i, err)
}
} else {
if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "isbn13") {
t.Fatalf("Index: %d ISBN13 failed Error: %s", i, err)
}
}
}
}
func TestISBN10Validation(t *testing.T) {
tests := []struct {
param string
expected bool
}{
{"", false},
{"foo", false},
{"3423214121", false},
{"978-3836221191", false},
{"3-423-21412-1", false},
{"3 423 21412 1", false},
{"3836221195", true},
{"1-61729-085-8", true},
{"3 423 21412 0", true},
{"3 401 01319 X", true},
}
for i, test := range tests {
err := validate.Field(test.param, "isbn10")
if test.expected == true {
if !IsEqual(t, err, nil) {
t.Fatalf("Index: %d ISBN10 failed Error: %s", i, err)
}
} else {
if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "isbn10") {
t.Fatalf("Index: %d ISBN10 failed Error: %s", i, err)
}
}
}
}
func TestExcludesRuneValidation(t *testing.T) {
tests := []struct {

Loading…
Cancel
Save