add omit empty check

allow for optional value checking
pull/16/head
Dean Karn 10 years ago
parent 1b249998b2
commit 05d2507495
  1. 193
      baked_in.go
  2. 14
      validator.go
  3. 2
      validator_test.go

@ -1,12 +1,203 @@
package validator
import "reflect"
import (
"log"
"reflect"
"regexp"
"strconv"
)
var bakedInValidators = map[string]ValidationFunc{
"required": isRequired,
"length": length,
"min": min,
"max": max,
"regex": regex,
}
func isRequired(field interface{}, param string) bool {
return field != nil && field != reflect.Zero(reflect.TypeOf(field)).Interface()
}
// length tests whether a variable's length is equal to a given
// value. For strings it tests the number of characters whereas
// for maps and slices it tests the number of items.
func length(field interface{}, param string) bool {
st := reflect.ValueOf(field)
switch st.Kind() {
case reflect.String:
p := asInt(param)
return int64(len(st.String())) == p
case reflect.Slice, reflect.Map, reflect.Array:
p := asInt(param)
return int64(st.Len()) == p
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
p := asInt(param)
return st.Int() == p
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
p := asUint(param)
return st.Uint() == p
case reflect.Float32, reflect.Float64:
p := asFloat(param)
return st.Float() == p
default:
log.Fatalf("Bad field type for Input Param %s for %s\n", param, field)
return false
}
}
// min tests whether a variable value is larger or equal to a given
// number. For number types, it's a simple lesser-than test; for
// strings it tests the number of characters whereas for maps
// and slices it tests the number of items.
func min(field interface{}, param string) bool {
st := reflect.ValueOf(field)
switch st.Kind() {
case reflect.String:
p := asInt(param)
return int64(len(st.String())) >= p
case reflect.Slice, reflect.Map, reflect.Array:
p := asInt(param)
return int64(st.Len()) >= p
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
p := asInt(param)
return st.Int() >= p
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
p := asUint(param)
return st.Uint() >= p
case reflect.Float32, reflect.Float64:
p := asFloat(param)
return st.Float() >= p
default:
log.Fatalf("Bad field type for Input Param %s for %s\n", param, field)
return false
}
}
// max tests whether a variable value is lesser than a given
// value. For numbers, it's a simple lesser-than test; for
// strings it tests the number of characters whereas for maps
// and slices it tests the number of items.
func max(field interface{}, param string) bool {
st := reflect.ValueOf(field)
switch st.Kind() {
case reflect.String:
p := asInt(param)
return int64(len(st.String())) <= p
case reflect.Slice, reflect.Map, reflect.Array:
p := asInt(param)
return int64(st.Len()) <= p
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
p := asInt(param)
return st.Int() <= p
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
p := asUint(param)
return st.Uint() <= p
case reflect.Float32, reflect.Float64:
p := asFloat(param)
return st.Float() <= p
default:
log.Fatalf("Bad field type for Input Param %s for %s\n", param, field)
return false
}
}
// regex is the builtin validation function that checks
// whether the string variable matches a regular expression
func regex(field interface{}, param string) bool {
s, ok := field.(string)
if !ok {
log.Fatalf("Bad field type %s\n", field)
}
re, err := regexp.Compile(param)
if err != nil {
log.Fatalf("Bad regex param %s\n", param)
}
if !re.MatchString(s) {
return false
}
return true
}
// asInt retuns the parameter as a int64
// or panics if it can't convert
func asInt(param string) int64 {
i, err := strconv.ParseInt(param, 0, 64)
if err != nil {
log.Fatalf("Bad Input Param %s\n", param)
}
return i
}
// asUint returns the parameter as a uint64
// or panics if it can't convert
func asUint(param string) uint64 {
i, err := strconv.ParseUint(param, 0, 64)
if err != nil {
log.Fatalf("Bad Input Param %s\n", param)
}
return i
}
// asFloat returns the parameter as a float64
// or panics if it can't convert
func asFloat(param string) float64 {
i, err := strconv.ParseFloat(param, 64)
if err != nil {
log.Fatalf("Bad Input Param %s\n", param)
}
return i
}

@ -8,6 +8,10 @@ import (
"unicode"
)
const (
omitempty string = "omitempty"
)
// FieldValidationError contains a single fields validation error
type FieldValidationError struct {
Field string
@ -161,7 +165,7 @@ func (v *Validator) ValidateStruct(s interface{}) map[string]*StructValidationEr
}
}
if currentStructError.Errors != nil {
if len(currentStructError.Errors) > 0 {
errorArray[currentStructError.Struct] = currentStructError
// free up memory
currentStructError = nil
@ -206,6 +210,10 @@ func (v *Validator) validateFieldByNameAndTag(f interface{}, name string, tag st
return nil
}
if strings.Contains(tag, omitempty) && !isRequired(f, "") {
return nil
}
valueField := reflect.ValueOf(f)
if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
@ -229,6 +237,10 @@ func (v *Validator) validateFieldByNameAndTag(f interface{}, name string, tag st
log.Fatalf("Invalid validation tag on field %s", name)
}
if key == omitempty {
continue
}
valFunc := v.validationFuncs[key]
if valFunc == nil {
log.Fatalf("Undefined validation function on field %s", name)

@ -8,7 +8,7 @@ import (
)
type UserDetails struct {
Address string `validate:"required"`
Address string `validate:"omitempty,length=6"`
}
type User struct {

Loading…
Cancel
Save