Skip to content

Commit

Permalink
feat: add Convert
Browse files Browse the repository at this point in the history
This PR improves the issues @ldemailly had with its implementation
  • Loading branch information
ccoVeille committed Sep 18, 2024
1 parent 390ade6 commit 8749dd5
Show file tree
Hide file tree
Showing 3 changed files with 2,815 additions and 0 deletions.
18 changes: 18 additions & 0 deletions examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,21 @@ func ExampleToUint() {
// 42
// conversion issue: -1 (int8) is less than 0 (uint): minimum value for this type exceeded
}

func ExampleConvert() {
a := int(42)
i, err := safecast.Convert[int32](a)
if err != nil {
fmt.Println(err)
}
fmt.Println(i)

a = int(math.MaxUint32 + 1)
_, err = safecast.ToInt32(a)
if err != nil {
fmt.Println(err)
}
// Output:
// 42
// conversion issue: 4294967296 (int) is greater than 2147483647 (int32): maximum value for this type exceeded
}
103 changes: 103 additions & 0 deletions generics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package safecast

import (
"math"
)

func Negative[T Type](t T) bool {
return t < 0
}

func SameSign[T1, T2 Type](a T1, b T2) bool {
return Negative(a) == Negative(b)
}

// idea: @ccoVeille (me)
// Suggestion of improvement by @ldemailly
// Fixed floats issues by @ccoveille (me)

func Convert[NumOut Type, NumIn Type](orig NumIn) (converted NumOut, err error) {
converted = NumOut(orig)

if !SameSign(orig, converted) {
err = ErrConversionIssue
return
}

base := orig
switch f := any(orig).(type) {
case float64:
base = NumIn(math.Trunc(f))
case float32:
base = NumIn(math.Trunc(float64(f)))
}

if NumIn(converted) == base {
return converted, nil
}

errBoundary := ErrExceedMaximumValue
boundary := getUpperBoundary(converted)
if Negative(orig) {
errBoundary = ErrExceedMinimumValue
boundary = getLowerBoundary(converted)

Check warning on line 43 in generics.go

View check run for this annotation

Codecov / codecov/patch

generics.go#L42-L43

Added lines #L42 - L43 were not covered by tests
}

return converted, Error{
value: orig,
err: errBoundary,
boundary: boundary,
}
}

func getUpperBoundary(value any) any {
switch value.(type) {
case float64:
return float64(math.MaxFloat64)
case float32:
return float32(math.MaxFloat32)
case int64:
return int64(math.MaxInt64)
case int32:
return int32(math.MaxInt32)
case int16:
return int16(math.MaxInt16)
case int8:
return int8(math.MaxInt8)
case int:
return int(math.MaxInt)

Check warning on line 68 in generics.go

View check run for this annotation

Codecov / codecov/patch

generics.go#L55-L68

Added lines #L55 - L68 were not covered by tests
case uint64:
return uint64(math.MaxUint64)
case uint32:
return uint32(math.MaxUint32)
case uint16:
return uint16(math.MaxUint16)
case uint8:
return uint8(math.MaxUint8)
case uint:
return uint(math.MaxUint)
}

return nil

Check warning on line 81 in generics.go

View check run for this annotation

Codecov / codecov/patch

generics.go#L81

Added line #L81 was not covered by tests
}

func getLowerBoundary(value any) any {
switch value.(type) {
case float64:
return -math.MaxFloat64
case float32:
return -math.MaxFloat32
case int64:
return math.MinInt64
case int32:
return math.MinInt32
case int16:
return math.MinInt16
case int8:
return math.MinInt8
case int:
return math.MinInt

Check warning on line 99 in generics.go

View check run for this annotation

Codecov / codecov/patch

generics.go#L84-L99

Added lines #L84 - L99 were not covered by tests
}

return 0

Check warning on line 102 in generics.go

View check run for this annotation

Codecov / codecov/patch

generics.go#L102

Added line #L102 was not covered by tests
}
Loading

0 comments on commit 8749dd5

Please sign in to comment.