minmax.go raw

   1  // Copyright 2016 Qiang Xue. All rights reserved.
   2  // Use of this source code is governed by a MIT-style
   3  // license that can be found in the LICENSE file.
   4  
   5  package validation
   6  
   7  import (
   8  	"fmt"
   9  	"reflect"
  10  	"time"
  11  )
  12  
  13  var (
  14  	// ErrMinGreaterEqualThanRequired is the error that returns when a value is less than a specified threshold.
  15  	ErrMinGreaterEqualThanRequired = NewError("validation_min_greater_equal_than_required", "must be no less than {{.threshold}}")
  16  	// ErrMaxLessEqualThanRequired is the error that returns when a value is greater than a specified threshold.
  17  	ErrMaxLessEqualThanRequired = NewError("validation_max_less_equal_than_required", "must be no greater than {{.threshold}}")
  18  	// ErrMinGreaterThanRequired is the error that returns when a value is less than or equal to a specified threshold.
  19  	ErrMinGreaterThanRequired = NewError("validation_min_greater_than_required", "must be greater than {{.threshold}}")
  20  	// ErrMaxLessThanRequired is the error that returns when a value is greater than or equal to a specified threshold.
  21  	ErrMaxLessThanRequired = NewError("validation_max_less_than_required", "must be less than {{.threshold}}")
  22  )
  23  
  24  // ThresholdRule is a validation rule that checks if a value satisfies the specified threshold requirement.
  25  type ThresholdRule struct {
  26  	threshold interface{}
  27  	operator  int
  28  	err       Error
  29  }
  30  
  31  const (
  32  	greaterThan = iota
  33  	greaterEqualThan
  34  	lessThan
  35  	lessEqualThan
  36  )
  37  
  38  // Min returns a validation rule that checks if a value is greater or equal than the specified value.
  39  // By calling Exclusive, the rule will check if the value is strictly greater than the specified value.
  40  // Note that the value being checked and the threshold value must be of the same type.
  41  // Only int, uint, float and time.Time types are supported.
  42  // An empty value is considered valid. Please use the Required rule to make sure a value is not empty.
  43  func Min(min interface{}) ThresholdRule {
  44  	return ThresholdRule{
  45  		threshold: min,
  46  		operator:  greaterEqualThan,
  47  		err:       ErrMinGreaterEqualThanRequired,
  48  	}
  49  
  50  }
  51  
  52  // Max returns a validation rule that checks if a value is less or equal than the specified value.
  53  // By calling Exclusive, the rule will check if the value is strictly less than the specified value.
  54  // Note that the value being checked and the threshold value must be of the same type.
  55  // Only int, uint, float and time.Time types are supported.
  56  // An empty value is considered valid. Please use the Required rule to make sure a value is not empty.
  57  func Max(max interface{}) ThresholdRule {
  58  	return ThresholdRule{
  59  		threshold: max,
  60  		operator:  lessEqualThan,
  61  		err:       ErrMaxLessEqualThanRequired,
  62  	}
  63  }
  64  
  65  // Exclusive sets the comparison to exclude the boundary value.
  66  func (r ThresholdRule) Exclusive() ThresholdRule {
  67  	if r.operator == greaterEqualThan {
  68  		r.operator = greaterThan
  69  		r.err = ErrMinGreaterThanRequired
  70  	} else if r.operator == lessEqualThan {
  71  		r.operator = lessThan
  72  		r.err = ErrMaxLessThanRequired
  73  	}
  74  	return r
  75  }
  76  
  77  // Validate checks if the given value is valid or not.
  78  func (r ThresholdRule) Validate(value interface{}) error {
  79  	value, isNil := Indirect(value)
  80  	if isNil || IsEmpty(value) {
  81  		return nil
  82  	}
  83  
  84  	rv := reflect.ValueOf(r.threshold)
  85  	switch rv.Kind() {
  86  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  87  		v, err := ToInt(value)
  88  		if err != nil {
  89  			return err
  90  		}
  91  		if r.compareInt(rv.Int(), v) {
  92  			return nil
  93  		}
  94  
  95  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
  96  		v, err := ToUint(value)
  97  		if err != nil {
  98  			return err
  99  		}
 100  		if r.compareUint(rv.Uint(), v) {
 101  			return nil
 102  		}
 103  
 104  	case reflect.Float32, reflect.Float64:
 105  		v, err := ToFloat(value)
 106  		if err != nil {
 107  			return err
 108  		}
 109  		if r.compareFloat(rv.Float(), v) {
 110  			return nil
 111  		}
 112  
 113  	case reflect.Struct:
 114  		t, ok := r.threshold.(time.Time)
 115  		if !ok {
 116  			return fmt.Errorf("type not supported: %v", rv.Type())
 117  		}
 118  		v, ok := value.(time.Time)
 119  		if !ok {
 120  			return fmt.Errorf("cannot convert %v to time.Time", reflect.TypeOf(value))
 121  		}
 122  		if v.IsZero() || r.compareTime(t, v) {
 123  			return nil
 124  		}
 125  
 126  	default:
 127  		return fmt.Errorf("type not supported: %v", rv.Type())
 128  	}
 129  
 130  	return r.err.SetParams(map[string]interface{}{"threshold": r.threshold})
 131  }
 132  
 133  // Error sets the error message for the rule.
 134  func (r ThresholdRule) Error(message string) ThresholdRule {
 135  	r.err = r.err.SetMessage(message)
 136  	return r
 137  }
 138  
 139  // ErrorObject sets the error struct for the rule.
 140  func (r ThresholdRule) ErrorObject(err Error) ThresholdRule {
 141  	r.err = err
 142  	return r
 143  }
 144  
 145  func (r ThresholdRule) compareInt(threshold, value int64) bool {
 146  	switch r.operator {
 147  	case greaterThan:
 148  		return value > threshold
 149  	case greaterEqualThan:
 150  		return value >= threshold
 151  	case lessThan:
 152  		return value < threshold
 153  	default:
 154  		return value <= threshold
 155  	}
 156  }
 157  
 158  func (r ThresholdRule) compareUint(threshold, value uint64) bool {
 159  	switch r.operator {
 160  	case greaterThan:
 161  		return value > threshold
 162  	case greaterEqualThan:
 163  		return value >= threshold
 164  	case lessThan:
 165  		return value < threshold
 166  	default:
 167  		return value <= threshold
 168  	}
 169  }
 170  
 171  func (r ThresholdRule) compareFloat(threshold, value float64) bool {
 172  	switch r.operator {
 173  	case greaterThan:
 174  		return value > threshold
 175  	case greaterEqualThan:
 176  		return value >= threshold
 177  	case lessThan:
 178  		return value < threshold
 179  	default:
 180  		return value <= threshold
 181  	}
 182  }
 183  
 184  func (r ThresholdRule) compareTime(threshold, value time.Time) bool {
 185  	switch r.operator {
 186  	case greaterThan:
 187  		return value.After(threshold)
 188  	case greaterEqualThan:
 189  		return value.After(threshold) || value.Equal(threshold)
 190  	case lessThan:
 191  		return value.Before(threshold)
 192  	default:
 193  		return value.Before(threshold) || value.Equal(threshold)
 194  	}
 195  }
 196