271 lines
6.6 KiB
Go
271 lines
6.6 KiB
Go
// Package validator implements value validations
|
|
//
|
|
// Copyright 2014 Roberto Teixeira <robteix@robteix.com>
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package validator
|
|
|
|
import (
|
|
"reflect"
|
|
"regexp"
|
|
"strconv"
|
|
)
|
|
|
|
// nonzero tests whether a variable value non-zero
|
|
// as defined by the golang spec.
|
|
func nonzero(v interface{}, param string) error {
|
|
st := reflect.ValueOf(v)
|
|
valid := true
|
|
switch st.Kind() {
|
|
case reflect.String:
|
|
valid = len(st.String()) != 0
|
|
case reflect.Ptr, reflect.Interface:
|
|
valid = !st.IsNil()
|
|
case reflect.Slice, reflect.Map, reflect.Array:
|
|
valid = st.Len() != 0
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
valid = st.Int() != 0
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
valid = st.Uint() != 0
|
|
case reflect.Float32, reflect.Float64:
|
|
valid = st.Float() != 0
|
|
case reflect.Bool:
|
|
valid = st.Bool()
|
|
case reflect.Invalid:
|
|
valid = false // always invalid
|
|
case reflect.Struct:
|
|
valid = true // always valid since only nil pointers are empty
|
|
default:
|
|
return ErrUnsupported
|
|
}
|
|
|
|
if !valid {
|
|
return ErrZeroValue
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// 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(v interface{}, param string) error {
|
|
st := reflect.ValueOf(v)
|
|
valid := true
|
|
if st.Kind() == reflect.Ptr {
|
|
if st.IsNil() {
|
|
return nil
|
|
}
|
|
st = st.Elem()
|
|
}
|
|
switch st.Kind() {
|
|
case reflect.String:
|
|
p, err := asInt(param)
|
|
if err != nil {
|
|
return ErrBadParameter
|
|
}
|
|
valid = int64(len(st.String())) == p
|
|
case reflect.Slice, reflect.Map, reflect.Array:
|
|
p, err := asInt(param)
|
|
if err != nil {
|
|
return ErrBadParameter
|
|
}
|
|
valid = int64(st.Len()) == p
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
p, err := asInt(param)
|
|
if err != nil {
|
|
return ErrBadParameter
|
|
}
|
|
valid = st.Int() == p
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
p, err := asUint(param)
|
|
if err != nil {
|
|
return ErrBadParameter
|
|
}
|
|
valid = st.Uint() == p
|
|
case reflect.Float32, reflect.Float64:
|
|
p, err := asFloat(param)
|
|
if err != nil {
|
|
return ErrBadParameter
|
|
}
|
|
valid = st.Float() == p
|
|
default:
|
|
return ErrUnsupported
|
|
}
|
|
if !valid {
|
|
return ErrLen
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// 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(v interface{}, param string) error {
|
|
st := reflect.ValueOf(v)
|
|
invalid := false
|
|
if st.Kind() == reflect.Ptr {
|
|
if st.IsNil() {
|
|
return nil
|
|
}
|
|
st = st.Elem()
|
|
}
|
|
switch st.Kind() {
|
|
case reflect.String:
|
|
p, err := asInt(param)
|
|
if err != nil {
|
|
return ErrBadParameter
|
|
}
|
|
invalid = int64(len(st.String())) < p
|
|
case reflect.Slice, reflect.Map, reflect.Array:
|
|
p, err := asInt(param)
|
|
if err != nil {
|
|
return ErrBadParameter
|
|
}
|
|
invalid = int64(st.Len()) < p
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
p, err := asInt(param)
|
|
if err != nil {
|
|
return ErrBadParameter
|
|
}
|
|
invalid = st.Int() < p
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
p, err := asUint(param)
|
|
if err != nil {
|
|
return ErrBadParameter
|
|
}
|
|
invalid = st.Uint() < p
|
|
case reflect.Float32, reflect.Float64:
|
|
p, err := asFloat(param)
|
|
if err != nil {
|
|
return ErrBadParameter
|
|
}
|
|
invalid = st.Float() < p
|
|
default:
|
|
return ErrUnsupported
|
|
}
|
|
if invalid {
|
|
return ErrMin
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// 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(v interface{}, param string) error {
|
|
st := reflect.ValueOf(v)
|
|
var invalid bool
|
|
if st.Kind() == reflect.Ptr {
|
|
if st.IsNil() {
|
|
return nil
|
|
}
|
|
st = st.Elem()
|
|
}
|
|
switch st.Kind() {
|
|
case reflect.String:
|
|
p, err := asInt(param)
|
|
if err != nil {
|
|
return ErrBadParameter
|
|
}
|
|
invalid = int64(len(st.String())) > p
|
|
case reflect.Slice, reflect.Map, reflect.Array:
|
|
p, err := asInt(param)
|
|
if err != nil {
|
|
return ErrBadParameter
|
|
}
|
|
invalid = int64(st.Len()) > p
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
p, err := asInt(param)
|
|
if err != nil {
|
|
return ErrBadParameter
|
|
}
|
|
invalid = st.Int() > p
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
p, err := asUint(param)
|
|
if err != nil {
|
|
return ErrBadParameter
|
|
}
|
|
invalid = st.Uint() > p
|
|
case reflect.Float32, reflect.Float64:
|
|
p, err := asFloat(param)
|
|
if err != nil {
|
|
return ErrBadParameter
|
|
}
|
|
invalid = st.Float() > p
|
|
default:
|
|
return ErrUnsupported
|
|
}
|
|
if invalid {
|
|
return ErrMax
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// regex is the builtin validation function that checks
|
|
// whether the string variable matches a regular expression
|
|
func regex(v interface{}, param string) error {
|
|
s, ok := v.(string)
|
|
if !ok {
|
|
sptr, ok := v.(*string)
|
|
if !ok {
|
|
return ErrUnsupported
|
|
}
|
|
if sptr == nil {
|
|
return nil
|
|
}
|
|
s = *sptr
|
|
}
|
|
|
|
re, err := regexp.Compile(param)
|
|
if err != nil {
|
|
return ErrBadParameter
|
|
}
|
|
|
|
if !re.MatchString(s) {
|
|
return ErrRegexp
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// asInt retuns the parameter as a int64
|
|
// or panics if it can't convert
|
|
func asInt(param string) (int64, error) {
|
|
i, err := strconv.ParseInt(param, 0, 64)
|
|
if err != nil {
|
|
return 0, ErrBadParameter
|
|
}
|
|
return i, nil
|
|
}
|
|
|
|
// asUint retuns the parameter as a uint64
|
|
// or panics if it can't convert
|
|
func asUint(param string) (uint64, error) {
|
|
i, err := strconv.ParseUint(param, 0, 64)
|
|
if err != nil {
|
|
return 0, ErrBadParameter
|
|
}
|
|
return i, nil
|
|
}
|
|
|
|
// asFloat retuns the parameter as a float64
|
|
// or panics if it can't convert
|
|
func asFloat(param string) (float64, error) {
|
|
i, err := strconv.ParseFloat(param, 64)
|
|
if err != nil {
|
|
return 0.0, ErrBadParameter
|
|
}
|
|
return i, nil
|
|
}
|