153 lines
3.6 KiB
Go
153 lines
3.6 KiB
Go
|
// Copyright 2011 Aaron Jacobs. All Rights Reserved.
|
||
|
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
||
|
//
|
||
|
// 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 oglematchers
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"math"
|
||
|
"reflect"
|
||
|
)
|
||
|
|
||
|
// LessThan returns a matcher that matches integer, floating point, or strings
|
||
|
// values v such that v < x. Comparison is not defined between numeric and
|
||
|
// string types, but is defined between all integer and floating point types.
|
||
|
//
|
||
|
// x must itself be an integer, floating point, or string type; otherwise,
|
||
|
// LessThan will panic.
|
||
|
func LessThan(x interface{}) Matcher {
|
||
|
v := reflect.ValueOf(x)
|
||
|
kind := v.Kind()
|
||
|
|
||
|
switch {
|
||
|
case isInteger(v):
|
||
|
case isFloat(v):
|
||
|
case kind == reflect.String:
|
||
|
|
||
|
default:
|
||
|
panic(fmt.Sprintf("LessThan: unexpected kind %v", kind))
|
||
|
}
|
||
|
|
||
|
return &lessThanMatcher{v}
|
||
|
}
|
||
|
|
||
|
type lessThanMatcher struct {
|
||
|
limit reflect.Value
|
||
|
}
|
||
|
|
||
|
func (m *lessThanMatcher) Description() string {
|
||
|
// Special case: make it clear that strings are strings.
|
||
|
if m.limit.Kind() == reflect.String {
|
||
|
return fmt.Sprintf("less than \"%s\"", m.limit.String())
|
||
|
}
|
||
|
|
||
|
return fmt.Sprintf("less than %v", m.limit.Interface())
|
||
|
}
|
||
|
|
||
|
func compareIntegers(v1, v2 reflect.Value) (err error) {
|
||
|
err = errors.New("")
|
||
|
|
||
|
switch {
|
||
|
case isSignedInteger(v1) && isSignedInteger(v2):
|
||
|
if v1.Int() < v2.Int() {
|
||
|
err = nil
|
||
|
}
|
||
|
return
|
||
|
|
||
|
case isSignedInteger(v1) && isUnsignedInteger(v2):
|
||
|
if v1.Int() < 0 || uint64(v1.Int()) < v2.Uint() {
|
||
|
err = nil
|
||
|
}
|
||
|
return
|
||
|
|
||
|
case isUnsignedInteger(v1) && isSignedInteger(v2):
|
||
|
if v1.Uint() <= math.MaxInt64 && int64(v1.Uint()) < v2.Int() {
|
||
|
err = nil
|
||
|
}
|
||
|
return
|
||
|
|
||
|
case isUnsignedInteger(v1) && isUnsignedInteger(v2):
|
||
|
if v1.Uint() < v2.Uint() {
|
||
|
err = nil
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
panic(fmt.Sprintf("compareIntegers: %v %v", v1, v2))
|
||
|
}
|
||
|
|
||
|
func getFloat(v reflect.Value) float64 {
|
||
|
switch {
|
||
|
case isSignedInteger(v):
|
||
|
return float64(v.Int())
|
||
|
|
||
|
case isUnsignedInteger(v):
|
||
|
return float64(v.Uint())
|
||
|
|
||
|
case isFloat(v):
|
||
|
return v.Float()
|
||
|
}
|
||
|
|
||
|
panic(fmt.Sprintf("getFloat: %v", v))
|
||
|
}
|
||
|
|
||
|
func (m *lessThanMatcher) Matches(c interface{}) (err error) {
|
||
|
v1 := reflect.ValueOf(c)
|
||
|
v2 := m.limit
|
||
|
|
||
|
err = errors.New("")
|
||
|
|
||
|
// Handle strings as a special case.
|
||
|
if v1.Kind() == reflect.String && v2.Kind() == reflect.String {
|
||
|
if v1.String() < v2.String() {
|
||
|
err = nil
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// If we get here, we require that we are dealing with integers or floats.
|
||
|
v1Legal := isInteger(v1) || isFloat(v1)
|
||
|
v2Legal := isInteger(v2) || isFloat(v2)
|
||
|
if !v1Legal || !v2Legal {
|
||
|
err = NewFatalError("which is not comparable")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Handle the various comparison cases.
|
||
|
switch {
|
||
|
// Both integers
|
||
|
case isInteger(v1) && isInteger(v2):
|
||
|
return compareIntegers(v1, v2)
|
||
|
|
||
|
// At least one float32
|
||
|
case v1.Kind() == reflect.Float32 || v2.Kind() == reflect.Float32:
|
||
|
if float32(getFloat(v1)) < float32(getFloat(v2)) {
|
||
|
err = nil
|
||
|
}
|
||
|
return
|
||
|
|
||
|
// At least one float64
|
||
|
case v1.Kind() == reflect.Float64 || v2.Kind() == reflect.Float64:
|
||
|
if getFloat(v1) < getFloat(v2) {
|
||
|
err = nil
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// We shouldn't get here.
|
||
|
panic(fmt.Sprintf("lessThanMatcher.Matches: Shouldn't get here: %v %v", v1, v2))
|
||
|
}
|