Skip to content

Commit

Permalink
Improve BoolE
Browse files Browse the repository at this point in the history
* improve performance for direct type
* support time.Duration
* support json.Number
  • Loading branch information
shockerli committed Sep 6, 2022
1 parent 21a699c commit 203a963
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 10 deletions.
34 changes: 32 additions & 2 deletions bool.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package cvt

import (
"encoding/json"
"reflect"
"strconv"
"strings"
"time"
)

// Bool convert an interface to a bool type, with default value
Expand All @@ -27,9 +29,39 @@ func BoolP(v interface{}, def ...bool) *bool {

// BoolE convert an interface to a bool type
func BoolE(val interface{}) (bool, error) {
// direct type(for improve performance)
switch vv := val.(type) {
case nil:
return false, nil
case bool:
return vv, nil
case
float32, float64:
return Float64(vv) != 0, nil
case
time.Duration,
int, int8, int16, int32, int64:
return Int64(vv) != 0, nil
case uint, uint8, uint16, uint32, uint64:
return Uint64(vv) != 0, nil
case []byte:
return str2bool(string(vv))
case string:
return str2bool(vv)
case json.Number:
vvv, err := vv.Float64()
if err != nil {
return false, newErr(val, "bool")
}
return vvv != 0, nil
}

// indirect type
v, rv := indirect(val)

switch vv := v.(type) {
case nil:
return false, nil
case bool:
return vv, nil
case int, int8, int16, int32, int64:
Expand All @@ -42,8 +74,6 @@ func BoolE(val interface{}) (bool, error) {
return str2bool(string(vv))
case string:
return str2bool(vv)
case nil:
return false, nil
}

switch rv.Kind() {
Expand Down
26 changes: 26 additions & 0 deletions bool_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package cvt_test

import (
"encoding/json"
"fmt"
"testing"
"time"

"github.com/shockerli/cvt"
)
Expand Down Expand Up @@ -37,6 +39,8 @@ func TestBool_HasDefault(t *testing.T) {
{&aliasTypeString0, true, false},
{aliasTypeStringOff, true, false},
{&aliasTypeStringOff, true, false},
{json.Number("0"), true, false},
{time.Duration(0), true, false},

{[]int{}, true, false},
{[]string{}, true, false},
Expand All @@ -61,6 +65,8 @@ func TestBool_HasDefault(t *testing.T) {
{&aliasTypeString1, false, true},
{aliasTypeStringOn, false, true},
{&aliasTypeStringOn, false, true},
{json.Number("12.0"), false, true},
{time.Duration(12), false, true},

{[]int{1, 2, 3}, false, true},
{[]string{"a", "b", "c"}, false, true},
Expand All @@ -75,6 +81,7 @@ func TestBool_HasDefault(t *testing.T) {
{testing.T{}, false, false},
{&testing.T{}, true, true},
{&testing.T{}, false, false},
{json.Number("hello"), true, true},
}

for i, tt := range tests {
Expand All @@ -100,6 +107,10 @@ func TestBool_BaseLine(t *testing.T) {
{map[int]string{}, false},
{aliasTypeString8d15Minus, true},
{&aliasTypeString8d15Minus, true},
{time.Duration(0), false},
{time.Duration(1), true},
{json.Number("0"), false},
{json.Number("10"), true},
}

for i, tt := range tests {
Expand Down Expand Up @@ -160,12 +171,20 @@ func TestBoolE(t *testing.T) {
{[]byte("Off"), false, false},
{aliasTypeInt0, false, false},
{&aliasTypeInt0, false, false},
{aliasTypeUint0, false, false},
{&aliasTypeUint0, false, false},
{aliasTypeFloat0, false, false},
{&aliasTypeFloat0, false, false},
{aliasTypeString0, false, false},
{&aliasTypeString0, false, false},
{aliasTypeStringOff, false, false},
{&aliasTypeStringOff, false, false},
{aliasTypeBool4False, false, false},
{&aliasTypeBool4False, false, false},
{json.Number("0"), false, false},
{json.Number("1"), true, false},
{time.Duration(0), false, false},
{time.Duration(1), true, false},

// false/slice/array/map
{[]int{}, false, false},
Expand All @@ -190,6 +209,11 @@ func TestBoolE(t *testing.T) {
{[]byte("true"), true, false},
{aliasTypeInt1, true, false},
{&aliasTypeInt1, true, false},
{aliasTypeUint1, true, false},
{&aliasTypeUint1, true, false},
{aliasTypeFloat1, true, false},
{&aliasTypeFloat1, true, false},
{aliasTypeBytesTrue, true, false},
{aliasTypeString1, true, false},
{&aliasTypeString1, true, false},
{aliasTypeStringOn, true, false},
Expand All @@ -214,6 +238,8 @@ func TestBoolE(t *testing.T) {
{"hello", false, true},
{testing.T{}, false, true},
{&testing.T{}, false, true},
{json.Number("hello"), false, true},
{aliasTypeBytesNil, false, true},
}

for i, tt := range tests {
Expand Down
57 changes: 53 additions & 4 deletions cvte_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ var (
aliasTypeUint0 AliasTypeUint = 0
aliasTypeUint1 AliasTypeUint = 1

aliasTypeFloat0 AliasTypeFloat64 = 0
aliasTypeFloat1 AliasTypeFloat64 = 1

aliasTypeString0 AliasTypeString = "0"
aliasTypeString1 AliasTypeString = "1"
aliasTypeString8d15 AliasTypeString = "8.15"
Expand All @@ -54,10 +57,11 @@ var (
aliasTypeStringLosePrecisionInt64 AliasTypeString = "7138826985640367621"
aliasTypeStringLosePrecisionFloat64 AliasTypeString = "7138826985640367621.18"

pointerRunes = []rune("中国")
pointerInterNil *AliasTypeInterface
pointerIntNil *AliasTypeInt
AliasTypeBytesNil AliasTypeBytes
pointerRunes = []rune("中国")
pointerInterNil *AliasTypeInterface
pointerIntNil *AliasTypeInt
aliasTypeBytesNil AliasTypeBytes
aliasTypeBytesTrue AliasTypeBytes = []byte("true")
)

// custom type
Expand Down Expand Up @@ -112,6 +116,51 @@ func Benchmark(b *testing.B) {

// [function tests]

func TestField_HasDefault(t *testing.T) {
tests := []struct {
input interface{}
field interface{}
def interface{}
expect interface{}
}{
// supported value, def is not used, def != expect
{TestStructC{C1: "c1"}, "C1", "C2", "c1"},

// unsupported value, def == expect
{TestStructC{C1: "c1"}, "C2", "c3", "c3"},
}

for i, tt := range tests {
msg := fmt.Sprintf("i = %d, input[%+v], def[%+v], expect[%+v]", i, tt.input, tt.def, tt.expect)

v := cvt.Field(tt.input, tt.field, tt.def)
assertEqual(t, tt.expect, v, "[NonE] "+msg)
}
}

func TestField_BaseLine(t *testing.T) {
tests := []struct {
input interface{}
field interface{}
expect interface{}
}{
{struct {
*TestStructC
}{
&TestStructC{C1: "c1"},
}, "C1", "c1"},

{"hello", "NONE", nil},
}

for i, tt := range tests {
msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v]", i, tt.input, tt.expect)

v := cvt.Field(tt.input, tt.field)
assertEqual(t, tt.expect, v, "[NonE] "+msg)
}
}

func TestFieldE(t *testing.T) {
tests := []struct {
input interface{}
Expand Down
26 changes: 26 additions & 0 deletions doc/content/en/type/bool.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,32 @@ Convert and store in a new bool value, and returns a pointer to it.
cvt.BoolP("true") // (*bool)(0x14000126180)(true)
```


## Conversion rule
### nil
Return `false`

### Bool
Return original value

### Number
> Integer, Float, and their derived type, such as `time.Duration` of `int64`
If `val != 0`, then return `true`; otherwise return `false`.

### String
> `string`, `[]byte`, and their derived type
### json.Number
If value can convert to `float64`, then compare `val != 0`; if not a number string, report an error.

### Array/Slice/Map
If the number of elements (len) greater than `0`, the return `true`; Return to `false` otherwise.

### Others
Other types, report an error.


## More Examples
More case see unit: `bool_test.go`

26 changes: 26 additions & 0 deletions doc/content/zh-cn/type/bool.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,32 @@ cvt.BoolE([]byte("true")) // true,nil
cvt.BoolP("true") // (*bool)(0x14000126180)(true)
```


## 规则
### nil
返回 `false`

### 布尔
原值

### 数字
> 整型、浮点型、及其衍生类型,比如 `int64` 的衍生类型之一 `time.Duration`
判定规则:`是否等于 0`,只要不等于零即为 `true`;否则为 `false`

### 字符串
> `string``[]byte`、及其衍生类型
### json.Number
其值如果是数字字符串,则可转换为 `float64` 再判定是否等于零;如果非数字字符串,则报错。

### Array/Slice/Map
如果其元素个数(len)大于零,即返回 `true`;否则返回 `false`

### 其他
不在上述已列类型中,即表示不支持,直接报错不支持。


## 更多示例
更多示例请看单元测试文件:`bool_test.go`

4 changes: 2 additions & 2 deletions slice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ func TestSliceFloat64_HasDefault(t *testing.T) {
func TestSliceFloat64_BaseLine(t *testing.T) {
tests := []struct {
input interface{}
expect []int
expect []float64
}{
{int(123), nil},
{uint16(123), nil},
Expand All @@ -334,7 +334,7 @@ func TestSliceFloat64_BaseLine(t *testing.T) {
for i, tt := range tests {
msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v]", i, tt.input, tt.expect)

v := cvt.SliceInt(tt.input)
v := cvt.SliceFloat64(tt.input)
assertEqual(t, tt.expect, v, "[NonE] "+msg)
}
}
Expand Down
4 changes: 2 additions & 2 deletions string_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@ func TestStringE(t *testing.T) {
{[]rune("我❤️中国"), "我❤️中国", false},
{nil, "", false},
{pointerInterNil, "", false},
{AliasTypeBytesNil, "", false},
{&AliasTypeBytesNil, "", false},
{aliasTypeBytesNil, "", false},
{&aliasTypeBytesNil, "", false},
{aliasTypeInt0, "0", false},
{&aliasTypeInt0, "0", false},
{aliasTypeInt1, "1", false},
Expand Down

1 comment on commit 203a963

@vercel
Copy link

@vercel vercel bot commented on 203a963 Sep 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.