Skip to content

Commit

Permalink
add String()/StringE()
Browse files Browse the repository at this point in the history
  • Loading branch information
shockerli committed Mar 10, 2021
1 parent a091ae3 commit 6d56ab3
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 0 deletions.
13 changes: 13 additions & 0 deletions cvt.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,16 @@ func Float32(v interface{}, def ...float32) float32 {

return 0
}

// String convert an interface to a string type, with default value
func String(v interface{}, def ...string) string {
if v, err := StringE(v); err == nil {
return v
}

if len(def) > 0 {
return def[0]
}

return ""
}
65 changes: 65 additions & 0 deletions cvt_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package cvt_test

import (
"errors"
"fmt"
"math"
"math/big"
"testing"
"time"

"github.com/stretchr/testify/assert"

Expand Down Expand Up @@ -1241,3 +1244,65 @@ func TestFloat32_BaseLine(t *testing.T) {
assert.Equal(t, tt.expect, v, msg)
}
}

func TestString_HasDefault(t *testing.T) {
tests := []struct {
input interface{}
def string
expect string
}{
// supported value, def is not used, def != expect
{uint64(8), "xxx", "8"},
{float32(8.31), "xxx", "8.31"},
{float64(-8.31), "xxx", "-8.31"},
{true, "xxx", "true"},
{int64(-8), "xxx", "-8"},
{[]byte("8.01"), "xxx", "8.01"},
{[]rune("我❤️中国"), "xxx", "我❤️中国"},
{nil, "xxx", ""},
{aliasTypeInt_0, "xxx", "0"},
{&aliasTypeString_8d15_minus, "xxx", "-8.15"},
{aliasTypeBool_true, "xxx", "true"},
{errors.New("errors"), "xxx", "errors"},
{time.Friday, "xxx", "Friday"},
{big.NewInt(123), "xxx", "123"},
{TestMarshalJSON{}, "xxx", "MarshalJSON"},
{&TestMarshalJSON{}, "xxx", "MarshalJSON"},

// unsupported value, def == expect
{testing.T{}, "xxx", "xxx"},
{&testing.T{}, "xxx", "xxx"},
{[]int{}, "xxx", "xxx"},
{[]string{}, "xxx", "xxx"},
{[...]string{}, "xxx", "xxx"},
{map[int]string{}, "xxx", "xxx"},
}

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.String(tt.input, tt.def)
assert.Equal(t, tt.expect, v, msg)
}
}

func TestString_BaseLine(t *testing.T) {
tests := []struct {
input interface{}
expect string
}{
{testing.T{}, ""},
{&testing.T{}, ""},
{[]int{}, ""},
{[]string{}, ""},
{[...]string{}, ""},
{map[int]string{}, ""},
}

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

v := cvt.String(tt.input)
assert.Equal(t, tt.expect, v, msg)
}
}
43 changes: 43 additions & 0 deletions cvte.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cvt

import (
"encoding/json"
"errors"
"fmt"
"math"
Expand Down Expand Up @@ -311,6 +312,48 @@ func Float32E(val interface{}) (float32, error) {
return float32(v), nil
}

// StringE convert an interface to a string type
func StringE(val interface{}) (string, error) {
v, _, rv := Indirect(val)

// interface implements
switch vv := val.(type) {
case fmt.Stringer:
return vv.String(), nil
case error:
return vv.Error(), nil
case json.Marshaler:
vvv, e := vv.MarshalJSON()
if e == nil {
return string(vvv), nil
}
}

// source type
switch vv := v.(type) {
case nil:
return "", nil
case bool:
return strconv.FormatBool(vv), nil
case string:
return vv, nil
case []byte:
return string(vv), nil
case []rune:
return string(vv), nil
case uint, uint8, uint16, uint32, uint64, uintptr:
return strconv.FormatUint(rv.Uint(), 10), nil
case int, int8, int16, int32, int64:
return strconv.FormatInt(rv.Int(), 10), nil
case float64:
return strconv.FormatFloat(vv, 'f', -1, 64), nil
case float32:
return strconv.FormatFloat(float64(vv), 'f', -1, 32), nil
}

return "", newErr(val, "string")
}

// Indirect returns the value with base type
func Indirect(a interface{}) (val interface{}, k reflect.Kind, v reflect.Value) {
if a == nil {
Expand Down
94 changes: 94 additions & 0 deletions cvte_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package cvt_test

import (
"errors"
"fmt"
"html/template"
"math"
"math/big"
"testing"
"time"

"github.com/stretchr/testify/assert"

Expand Down Expand Up @@ -38,6 +42,12 @@ var (
aliasTypeString_off AliasTypeString = "off"
)

type TestMarshalJSON struct{}

func (TestMarshalJSON) MarshalJSON() ([]byte, error) {
return []byte("MarshalJSON"), nil
}

func TestBoolE(t *testing.T) {
tests := []struct {
input interface{}
Expand Down Expand Up @@ -1208,3 +1218,87 @@ func TestFloat32E(t *testing.T) {
assert.Equal(t, tt.expect, v, msg)
}
}

func TestStringE(t *testing.T) {
tests := []struct {
input interface{}
expect string
isErr bool
}{
{int(8), "8", false},
{int8(8), "8", false},
{int16(8), "8", false},
{int32(8), "8", false},
{int64(8), "8", false},
{uint(8), "8", false},
{uint8(8), "8", false},
{uint16(8), "8", false},
{uint32(8), "8", false},
{uint64(8), "8", false},
{float32(8.31), "8.31", false},
{float64(8.31), "8.31", false},
{true, "true", false},
{false, "false", false},
{int(-8), "-8", false},
{int8(-8), "-8", false},
{int16(-8), "-8", false},
{int32(-8), "-8", false},
{int64(-8), "-8", false},
{float32(-8.31), "-8.31", false},
{float64(-8.31), "-8.31", false},
{[]byte("-8"), "-8", false},
{[]byte("-8.01"), "-8.01", false},
{[]byte("8"), "8", false},
{[]byte("8.00"), "8.00", false},
{[]byte("8.01"), "8.01", false},
{[]rune("我❤️中国"), "我❤️中国", false},
{nil, "", false},
{aliasTypeInt_0, "0", false},
{&aliasTypeInt_0, "0", false},
{aliasTypeInt_1, "1", false},
{&aliasTypeInt_1, "1", false},
{aliasTypeString_0, "0", false},
{&aliasTypeString_0, "0", false},
{aliasTypeString_1, "1", false},
{&aliasTypeString_1, "1", false},
{aliasTypeString_8d15, "8.15", false},
{&aliasTypeString_8d15, "8.15", false},
{aliasTypeString_8d15_minus, "-8.15", false},
{&aliasTypeString_8d15_minus, "-8.15", false},
{aliasTypeBool_true, "true", false},
{&aliasTypeBool_true, "true", false},
{aliasTypeBool_false, "false", false},
{&aliasTypeBool_false, "false", false},
{errors.New("errors"), "errors", false},
{time.Friday, "Friday", false},
{big.NewInt(123), "123", false},
{TestMarshalJSON{}, "MarshalJSON", false},
{&TestMarshalJSON{}, "MarshalJSON", false},
{template.URL("http://host.foo"), "http://host.foo", false},

// errors
{testing.T{}, "", true},
{&testing.T{}, "", true},
{[]int{}, "", true},
{[]string{}, "", true},
{[...]string{}, "", true},
{map[int]string{}, "", true},
}

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

v, err := cvt.StringE(tt.input)
if tt.isErr {
assert.Error(t, err, msg)
continue
}

assert.NoError(t, err, msg)
assert.Equal(t, tt.expect, v, msg)

// Non-E test with no default value:
v = cvt.String(tt.input)
assert.Equal(t, tt.expect, v, msg)
}
}

0 comments on commit 6d56ab3

Please sign in to comment.