Skip to content

Commit

Permalink
feat: add Convert
Browse files Browse the repository at this point in the history
and allow to convert from string and bool
  • Loading branch information
ccoVeille committed Dec 14, 2024
1 parent 992666e commit 982eb80
Show file tree
Hide file tree
Showing 4 changed files with 798 additions and 1 deletion.
82 changes: 81 additions & 1 deletion conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,55 @@
package safecast

import (
"fmt"
"math"
"strconv"
"strings"
)

func Convert[NumOut Number](orig any) (converted NumOut, err error) {
switch v := orig.(type) {
case int:
return convertFromNumber[NumOut](v)
case uint:
return convertFromNumber[NumOut](v)
case int8:
return convertFromNumber[NumOut](v)
case uint8:
return convertFromNumber[NumOut](v)
case int16:
return convertFromNumber[NumOut](v)
case uint16:
return convertFromNumber[NumOut](v)
case int32:
return convertFromNumber[NumOut](v)
case uint32:
return convertFromNumber[NumOut](v)
case int64:
return convertFromNumber[NumOut](v)
case uint64:
return convertFromNumber[NumOut](v)
case float32:
return convertFromNumber[NumOut](v)
case float64:
return convertFromNumber[NumOut](v)
case bool:
o := 0
if v {
o = 1
}
return NumOut(o), nil
case fmt.Stringer:
return convertFromString[NumOut](v.String())
case error:
return convertFromString[NumOut](v.Error())
case string:
return convertFromString[NumOut](v)
}

return 0, ErrConversionIssue
}

func convertFromNumber[NumOut Number, NumIn Number](orig NumIn) (converted NumOut, err error) {
converted = NumOut(orig)

Expand Down Expand Up @@ -85,7 +131,41 @@ func convertFromNumber[NumOut Number, NumIn Number](orig NumIn) (converted NumOu
}
}

// ToInt attempts to convert any [Number] value to an int.
func convertFromString[NumOut Number](s string) (converted NumOut, err error) {
s = strings.TrimSpace(s)

if b, err := strconv.ParseBool(s); err == nil {
if b {
return NumOut(1), nil
}
return NumOut(0), nil
}

if strings.Contains(s, ".") {
o, err := strconv.ParseFloat(s, 64)
if err != nil {
return 0, fmt.Errorf("%w: cannot convert %v to %T", ErrConversionIssue, s, converted)
}
return convertFromNumber[NumOut](o)
}

if s, found := strings.CutPrefix(s, "-"); found {
o, err := strconv.ParseInt(s, 0, 64)
if err != nil || o < 0 {
return 0, fmt.Errorf("%w: cannot convert %v to %T", ErrConversionIssue, s, converted)
}

return convertFromNumber[NumOut](-o)
}

o, err := strconv.ParseUint(s, 0, 64)
if err != nil {
return 0, fmt.Errorf("%w: cannot convert %v to %T", ErrConversionIssue, s, converted)
}
return convertFromNumber[NumOut](o)
}

// ToInt attempts to convert any [Type] value to an int.
// If the conversion results in a value outside the range of an int,
// an [ErrConversionIssue] error is returned.
func ToInt[T Number](i T) (int, error) {
Expand Down
21 changes: 21 additions & 0 deletions conversion_64bit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ package safecast_test
import (
"math"
"testing"

"github.com/ccoveille/go-safecast"
)

func TestToInt32_64bit(t *testing.T) {
Expand Down Expand Up @@ -53,3 +55,22 @@ func TestToInt_64bit(t *testing.T) {
})
})
}

// TestConvert_64bit completes the [TestConvert] tests in conversion_test.go
// it contains the tests that can only works on 64-bit systems
func TestConvert_64bit(t *testing.T) {
t.Run("to uint32", func(t *testing.T) {
for name, tt := range map[string]struct {
input any
want uint32
}{
"positive out of range": {input: uint64(math.MaxUint32 + 1), want: 0},
} {
t.Run(name, func(t *testing.T) {
got, err := safecast.Convert[uint32](tt.input)
assertEqual(t, tt.want, got)
requireErrorIs(t, err, safecast.ErrConversionIssue)
})
}
})
}
Loading

0 comments on commit 982eb80

Please sign in to comment.