added benchmarks; added lazyError and use lazyError instead of fmt.Errorf

This commit is contained in:
Yan Mikhaylov 2018-09-18 14:13:51 +03:00
parent 8965335b8c
commit 4797406421
4 changed files with 126 additions and 56 deletions

View File

@ -827,7 +827,7 @@ func TestToStringMapStringE(t *testing.T) {
var interfaceMapString = map[interface{}]string{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"} var interfaceMapString = map[interface{}]string{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"}
var interfaceMapInterface = map[interface{}]interface{}{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"} var interfaceMapInterface = map[interface{}]interface{}{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"}
var jsonString = `{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"}` var jsonString = `{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"}`
var invalidJsonString = `{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"` var invalidJSONString = `{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"`
var emptyString = "" var emptyString = ""
tests := []struct { tests := []struct {
@ -844,7 +844,7 @@ func TestToStringMapStringE(t *testing.T) {
// errors // errors
{nil, nil, true}, {nil, nil, true},
{testing.T{}, nil, true}, {testing.T{}, nil, true},
{invalidJsonString, nil, true}, {invalidJSONString, nil, true},
{emptyString, nil, true}, {emptyString, nil, true},
} }
@ -1080,11 +1080,41 @@ func TestToBoolE(t *testing.T) {
} }
func BenchmarkTooBool(b *testing.B) { func BenchmarkTooBool(b *testing.B) {
for i := 0; i < b.N; i++ { b.Run("valid bool(true)", func(b *testing.B) {
if !ToBool(true) { b.ReportAllocs()
b.Fatal("ToBool returned false") for i := 0; i < b.N; i++ {
if !ToBool(true) {
b.Fatal("ToBool returned false")
}
} }
} })
b.Run("valid bool(1)", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
if !ToBool(1) {
b.Fatal("ToBool returned false")
}
}
})
b.Run(`invalid bool("xxx")`, func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
if ToBool("xxx") {
b.Fatal("ToBool returned true")
}
}
})
b.Run(`invalid bool(1.0)`, func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
if ToBool(1.0) {
b.Fatal("ToBool returned true")
}
}
})
} }
func TestIndirectPointers(t *testing.T) { func TestIndirectPointers(t *testing.T) {

View File

@ -7,7 +7,6 @@ package cast
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"html/template" "html/template"
"reflect" "reflect"
@ -16,8 +15,6 @@ import (
"time" "time"
) )
var errNegativeNotAllowed = errors.New("unable to cast negative value")
// ToTimeE casts an interface to a time.Time type. // ToTimeE casts an interface to a time.Time type.
func ToTimeE(i interface{}) (tim time.Time, err error) { func ToTimeE(i interface{}) (tim time.Time, err error) {
i = indirect(i) i = indirect(i)
@ -40,7 +37,7 @@ func ToTimeE(i interface{}) (tim time.Time, err error) {
case uint32: case uint32:
return time.Unix(int64(v), 0), nil return time.Unix(int64(v), 0), nil
default: default:
return time.Time{}, fmt.Errorf("unable to cast %#v of type %T to Time", i, i) return time.Time{}, newError("unable to cast %#v of type %T to Time", i, i)
} }
} }
@ -65,7 +62,7 @@ func ToDurationE(i interface{}) (d time.Duration, err error) {
} }
return return
default: default:
err = fmt.Errorf("unable to cast %#v of type %T to Duration", i, i) err = newError("unable to cast %#v of type %T to Duration", i, i)
return return
} }
} }
@ -87,7 +84,7 @@ func ToBoolE(i interface{}) (bool, error) {
case string: case string:
return strconv.ParseBool(i.(string)) return strconv.ParseBool(i.(string))
default: default:
return false, fmt.Errorf("unable to cast %#v of type %T to bool", i, i) return false, newError("unable to cast %#v of type %T to bool", i, i)
} }
} }
@ -125,14 +122,14 @@ func ToFloat64E(i interface{}) (float64, error) {
if err == nil { if err == nil {
return v, nil return v, nil
} }
return 0, fmt.Errorf("unable to cast %#v of type %T to float64", i, i) return 0, newError("unable to cast %#v of type %T to float64", i, i)
case bool: case bool:
if s { if s {
return 1, nil return 1, nil
} }
return 0, nil return 0, nil
default: default:
return 0, fmt.Errorf("unable to cast %#v of type %T to float64", i, i) return 0, newError("unable to cast %#v of type %T to float64", i, i)
} }
} }
@ -170,14 +167,14 @@ func ToFloat32E(i interface{}) (float32, error) {
if err == nil { if err == nil {
return float32(v), nil return float32(v), nil
} }
return 0, fmt.Errorf("unable to cast %#v of type %T to float32", i, i) return 0, newError("unable to cast %#v of type %T to float32", i, i)
case bool: case bool:
if s { if s {
return 1, nil return 1, nil
} }
return 0, nil return 0, nil
default: default:
return 0, fmt.Errorf("unable to cast %#v of type %T to float32", i, i) return 0, newError("unable to cast %#v of type %T to float32", i, i)
} }
} }
@ -215,7 +212,7 @@ func ToInt64E(i interface{}) (int64, error) {
if err == nil { if err == nil {
return v, nil return v, nil
} }
return 0, fmt.Errorf("unable to cast %#v of type %T to int64", i, i) return 0, newError("unable to cast %#v of type %T to int64", i, i)
case bool: case bool:
if s { if s {
return 1, nil return 1, nil
@ -224,7 +221,7 @@ func ToInt64E(i interface{}) (int64, error) {
case nil: case nil:
return 0, nil return 0, nil
default: default:
return 0, fmt.Errorf("unable to cast %#v of type %T to int64", i, i) return 0, newError("unable to cast %#v of type %T to int64", i, i)
} }
} }
@ -262,7 +259,7 @@ func ToInt32E(i interface{}) (int32, error) {
if err == nil { if err == nil {
return int32(v), nil return int32(v), nil
} }
return 0, fmt.Errorf("unable to cast %#v of type %T to int32", i, i) return 0, newError("unable to cast %#v of type %T to int32", i, i)
case bool: case bool:
if s { if s {
return 1, nil return 1, nil
@ -271,7 +268,7 @@ func ToInt32E(i interface{}) (int32, error) {
case nil: case nil:
return 0, nil return 0, nil
default: default:
return 0, fmt.Errorf("unable to cast %#v of type %T to int32", i, i) return 0, newError("unable to cast %#v of type %T to int32", i, i)
} }
} }
@ -309,7 +306,7 @@ func ToInt16E(i interface{}) (int16, error) {
if err == nil { if err == nil {
return int16(v), nil return int16(v), nil
} }
return 0, fmt.Errorf("unable to cast %#v of type %T to int16", i, i) return 0, newError("unable to cast %#v of type %T to int16", i, i)
case bool: case bool:
if s { if s {
return 1, nil return 1, nil
@ -318,7 +315,7 @@ func ToInt16E(i interface{}) (int16, error) {
case nil: case nil:
return 0, nil return 0, nil
default: default:
return 0, fmt.Errorf("unable to cast %#v of type %T to int16", i, i) return 0, newError("unable to cast %#v of type %T to int16", i, i)
} }
} }
@ -356,7 +353,7 @@ func ToInt8E(i interface{}) (int8, error) {
if err == nil { if err == nil {
return int8(v), nil return int8(v), nil
} }
return 0, fmt.Errorf("unable to cast %#v of type %T to int8", i, i) return 0, newError("unable to cast %#v of type %T to int8", i, i)
case bool: case bool:
if s { if s {
return 1, nil return 1, nil
@ -365,7 +362,7 @@ func ToInt8E(i interface{}) (int8, error) {
case nil: case nil:
return 0, nil return 0, nil
default: default:
return 0, fmt.Errorf("unable to cast %#v of type %T to int8", i, i) return 0, newError("unable to cast %#v of type %T to int8", i, i)
} }
} }
@ -403,7 +400,7 @@ func ToIntE(i interface{}) (int, error) {
if err == nil { if err == nil {
return int(v), nil return int(v), nil
} }
return 0, fmt.Errorf("unable to cast %#v of type %T to int", i, i) return 0, newError("unable to cast %#v of type %T to int", i, i)
case bool: case bool:
if s { if s {
return 1, nil return 1, nil
@ -412,7 +409,7 @@ func ToIntE(i interface{}) (int, error) {
case nil: case nil:
return 0, nil return 0, nil
default: default:
return 0, fmt.Errorf("unable to cast %#v of type %T to int", i, i) return 0, newError("unable to cast %#v of type %T to int", i, i)
} }
} }
@ -426,7 +423,7 @@ func ToUintE(i interface{}) (uint, error) {
if err == nil { if err == nil {
return uint(v), nil return uint(v), nil
} }
return 0, fmt.Errorf("unable to cast %#v to uint: %s", i, err) return 0, newError("unable to cast %#v to uint: %s", i, err)
case int: case int:
if s < 0 { if s < 0 {
return 0, errNegativeNotAllowed return 0, errNegativeNotAllowed
@ -480,7 +477,7 @@ func ToUintE(i interface{}) (uint, error) {
case nil: case nil:
return 0, nil return 0, nil
default: default:
return 0, fmt.Errorf("unable to cast %#v of type %T to uint", i, i) return 0, newError("unable to cast %#v of type %T to uint", i, i)
} }
} }
@ -494,7 +491,7 @@ func ToUint64E(i interface{}) (uint64, error) {
if err == nil { if err == nil {
return v, nil return v, nil
} }
return 0, fmt.Errorf("unable to cast %#v to uint64: %s", i, err) return 0, newError("unable to cast %#v to uint64: %s", i, err)
case int: case int:
if s < 0 { if s < 0 {
return 0, errNegativeNotAllowed return 0, errNegativeNotAllowed
@ -548,7 +545,7 @@ func ToUint64E(i interface{}) (uint64, error) {
case nil: case nil:
return 0, nil return 0, nil
default: default:
return 0, fmt.Errorf("unable to cast %#v of type %T to uint64", i, i) return 0, newError("unable to cast %#v of type %T to uint64", i, i)
} }
} }
@ -562,7 +559,7 @@ func ToUint32E(i interface{}) (uint32, error) {
if err == nil { if err == nil {
return uint32(v), nil return uint32(v), nil
} }
return 0, fmt.Errorf("unable to cast %#v to uint32: %s", i, err) return 0, newError("unable to cast %#v to uint32: %s", i, err)
case int: case int:
if s < 0 { if s < 0 {
return 0, errNegativeNotAllowed return 0, errNegativeNotAllowed
@ -616,7 +613,7 @@ func ToUint32E(i interface{}) (uint32, error) {
case nil: case nil:
return 0, nil return 0, nil
default: default:
return 0, fmt.Errorf("unable to cast %#v of type %T to uint32", i, i) return 0, newError("unable to cast %#v of type %T to uint32", i, i)
} }
} }
@ -630,7 +627,7 @@ func ToUint16E(i interface{}) (uint16, error) {
if err == nil { if err == nil {
return uint16(v), nil return uint16(v), nil
} }
return 0, fmt.Errorf("unable to cast %#v to uint16: %s", i, err) return 0, newError("unable to cast %#v to uint16: %s", i, err)
case int: case int:
if s < 0 { if s < 0 {
return 0, errNegativeNotAllowed return 0, errNegativeNotAllowed
@ -684,7 +681,7 @@ func ToUint16E(i interface{}) (uint16, error) {
case nil: case nil:
return 0, nil return 0, nil
default: default:
return 0, fmt.Errorf("unable to cast %#v of type %T to uint16", i, i) return 0, newError("unable to cast %#v of type %T to uint16", i, i)
} }
} }
@ -698,7 +695,7 @@ func ToUint8E(i interface{}) (uint8, error) {
if err == nil { if err == nil {
return uint8(v), nil return uint8(v), nil
} }
return 0, fmt.Errorf("unable to cast %#v to uint8: %s", i, err) return 0, newError("unable to cast %#v to uint8: %s", i, err)
case int: case int:
if s < 0 { if s < 0 {
return 0, errNegativeNotAllowed return 0, errNegativeNotAllowed
@ -752,7 +749,7 @@ func ToUint8E(i interface{}) (uint8, error) {
case nil: case nil:
return 0, nil return 0, nil
default: default:
return 0, fmt.Errorf("unable to cast %#v of type %T to uint8", i, i) return 0, newError("unable to cast %#v of type %T to uint8", i, i)
} }
} }
@ -847,7 +844,7 @@ func ToStringE(i interface{}) (string, error) {
case error: case error:
return s.Error(), nil return s.Error(), nil
default: default:
return "", fmt.Errorf("unable to cast %#v of type %T to string", i, i) return "", newError("unable to cast %#v of type %T to string", i, i)
} }
} }
@ -877,7 +874,7 @@ func ToStringMapStringE(i interface{}) (map[string]string, error) {
err := jsonStringToObject(v, &m) err := jsonStringToObject(v, &m)
return m, err return m, err
default: default:
return m, fmt.Errorf("unable to cast %#v of type %T to map[string]string", i, i) return m, newError("unable to cast %#v of type %T to map[string]string", i, i)
} }
} }
@ -928,11 +925,11 @@ func ToStringMapStringSliceE(i interface{}) (map[string][]string, error) {
for k, val := range v { for k, val := range v {
key, err := ToStringE(k) key, err := ToStringE(k)
if err != nil { if err != nil {
return m, fmt.Errorf("unable to cast %#v of type %T to map[string][]string", i, i) return m, newError("unable to cast %#v of type %T to map[string][]string", i, i)
} }
value, err := ToStringSliceE(val) value, err := ToStringSliceE(val)
if err != nil { if err != nil {
return m, fmt.Errorf("unable to cast %#v of type %T to map[string][]string", i, i) return m, newError("unable to cast %#v of type %T to map[string][]string", i, i)
} }
m[key] = value m[key] = value
} }
@ -940,7 +937,7 @@ func ToStringMapStringSliceE(i interface{}) (map[string][]string, error) {
err := jsonStringToObject(v, &m) err := jsonStringToObject(v, &m)
return m, err return m, err
default: default:
return m, fmt.Errorf("unable to cast %#v of type %T to map[string][]string", i, i) return m, newError("unable to cast %#v of type %T to map[string][]string", i, i)
} }
return m, nil return m, nil
} }
@ -966,7 +963,7 @@ func ToStringMapBoolE(i interface{}) (map[string]bool, error) {
err := jsonStringToObject(v, &m) err := jsonStringToObject(v, &m)
return m, err return m, err
default: default:
return m, fmt.Errorf("unable to cast %#v of type %T to map[string]bool", i, i) return m, newError("unable to cast %#v of type %T to map[string]bool", i, i)
} }
} }
@ -986,7 +983,7 @@ func ToStringMapE(i interface{}) (map[string]interface{}, error) {
err := jsonStringToObject(v, &m) err := jsonStringToObject(v, &m)
return m, err return m, err
default: default:
return m, fmt.Errorf("unable to cast %#v of type %T to map[string]interface{}", i, i) return m, newError("unable to cast %#v of type %T to map[string]interface{}", i, i)
} }
} }
@ -1003,14 +1000,14 @@ func ToSliceE(i interface{}) ([]interface{}, error) {
} }
return s, nil return s, nil
default: default:
return s, fmt.Errorf("unable to cast %#v of type %T to []interface{}", i, i) return s, newError("unable to cast %#v of type %T to []interface{}", i, i)
} }
} }
// ToBoolSliceE casts an interface to a []bool type. // ToBoolSliceE casts an interface to a []bool type.
func ToBoolSliceE(i interface{}) ([]bool, error) { func ToBoolSliceE(i interface{}) ([]bool, error) {
if i == nil { if i == nil {
return []bool{}, fmt.Errorf("unable to cast %#v of type %T to []bool", i, i) return []bool{}, newError("unable to cast %#v of type %T to []bool", i, i)
} }
switch v := i.(type) { switch v := i.(type) {
@ -1026,13 +1023,13 @@ func ToBoolSliceE(i interface{}) ([]bool, error) {
for j := 0; j < s.Len(); j++ { for j := 0; j < s.Len(); j++ {
val, err := ToBoolE(s.Index(j).Interface()) val, err := ToBoolE(s.Index(j).Interface())
if err != nil { if err != nil {
return []bool{}, fmt.Errorf("unable to cast %#v of type %T to []bool", i, i) return []bool{}, newError("unable to cast %#v of type %T to []bool", i, i)
} }
a[j] = val a[j] = val
} }
return a, nil return a, nil
default: default:
return []bool{}, fmt.Errorf("unable to cast %#v of type %T to []bool", i, i) return []bool{}, newError("unable to cast %#v of type %T to []bool", i, i)
} }
} }
@ -1053,18 +1050,18 @@ func ToStringSliceE(i interface{}) ([]string, error) {
case interface{}: case interface{}:
str, err := ToStringE(v) str, err := ToStringE(v)
if err != nil { if err != nil {
return a, fmt.Errorf("unable to cast %#v of type %T to []string", i, i) return a, newError("unable to cast %#v of type %T to []string", i, i)
} }
return []string{str}, nil return []string{str}, nil
default: default:
return a, fmt.Errorf("unable to cast %#v of type %T to []string", i, i) return a, newError("unable to cast %#v of type %T to []string", i, i)
} }
} }
// ToIntSliceE casts an interface to a []int type. // ToIntSliceE casts an interface to a []int type.
func ToIntSliceE(i interface{}) ([]int, error) { func ToIntSliceE(i interface{}) ([]int, error) {
if i == nil { if i == nil {
return []int{}, fmt.Errorf("unable to cast %#v of type %T to []int", i, i) return []int{}, newError("unable to cast %#v of type %T to []int", i, i)
} }
switch v := i.(type) { switch v := i.(type) {
@ -1080,20 +1077,20 @@ func ToIntSliceE(i interface{}) ([]int, error) {
for j := 0; j < s.Len(); j++ { for j := 0; j < s.Len(); j++ {
val, err := ToIntE(s.Index(j).Interface()) val, err := ToIntE(s.Index(j).Interface())
if err != nil { if err != nil {
return []int{}, fmt.Errorf("unable to cast %#v of type %T to []int", i, i) return []int{}, newError("unable to cast %#v of type %T to []int", i, i)
} }
a[j] = val a[j] = val
} }
return a, nil return a, nil
default: default:
return []int{}, fmt.Errorf("unable to cast %#v of type %T to []int", i, i) return []int{}, newError("unable to cast %#v of type %T to []int", i, i)
} }
} }
// ToDurationSliceE casts an interface to a []time.Duration type. // ToDurationSliceE casts an interface to a []time.Duration type.
func ToDurationSliceE(i interface{}) ([]time.Duration, error) { func ToDurationSliceE(i interface{}) ([]time.Duration, error) {
if i == nil { if i == nil {
return []time.Duration{}, fmt.Errorf("unable to cast %#v of type %T to []time.Duration", i, i) return []time.Duration{}, newError("unable to cast %#v of type %T to []time.Duration", i, i)
} }
switch v := i.(type) { switch v := i.(type) {
@ -1109,13 +1106,13 @@ func ToDurationSliceE(i interface{}) ([]time.Duration, error) {
for j := 0; j < s.Len(); j++ { for j := 0; j < s.Len(); j++ {
val, err := ToDurationE(s.Index(j).Interface()) val, err := ToDurationE(s.Index(j).Interface())
if err != nil { if err != nil {
return []time.Duration{}, fmt.Errorf("unable to cast %#v of type %T to []time.Duration", i, i) return []time.Duration{}, newError("unable to cast %#v of type %T to []time.Duration", i, i)
} }
a[j] = val a[j] = val
} }
return a, nil return a, nil
default: default:
return []time.Duration{}, fmt.Errorf("unable to cast %#v of type %T to []time.Duration", i, i) return []time.Duration{}, newError("unable to cast %#v of type %T to []time.Duration", i, i)
} }
} }
@ -1155,7 +1152,7 @@ func parseDateWith(s string, dates []string) (d time.Time, e error) {
return return
} }
} }
return d, fmt.Errorf("unable to parse date: %s", s) return d, newError("unable to parse date: %s", s)
} }
// jsonStringToObject attempts to unmarshall a string as JSON into // jsonStringToObject attempts to unmarshall a string as JSON into

30
errors.go Normal file
View File

@ -0,0 +1,30 @@
package cast
import (
"errors"
"fmt"
"sync"
)
var errNegativeNotAllowed = errors.New("unable to cast negative value")
type lazyError struct {
format string
args []interface{}
once sync.Once
result string
}
func (e *lazyError) Error() string {
e.once.Do(func() {
e.result = fmt.Sprintf(e.format, e.args...)
})
return e.result
}
func newError(format string, args ...interface{}) error {
return &lazyError{
format: format,
args: args,
}
}

13
errors_test.go Normal file
View File

@ -0,0 +1,13 @@
package cast
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestLazyError_Error(t *testing.T) {
err := newError("foo")
assert.NotNil(t, err)
assert.Equal(t, err.Error(), "foo")
}