Skip to content

Commit

Permalink
doc: update documentation as errors messages changed
Browse files Browse the repository at this point in the history
  • Loading branch information
ccoVeille committed Sep 10, 2024
1 parent 25e698e commit 618c455
Show file tree
Hide file tree
Showing 4 changed files with 305 additions and 34 deletions.
106 changes: 73 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
[![codecov](https://codecov.io/gh/ccoVeille/go-safecast/graph/badge.svg?token=VW0VO503U6)](https://codecov.io/gh/ccoVeille/go-safecast)
[![Code Climate](https://codeclimate.com/github/ccoVeille/go-safecast.png)](https://codeclimate.com/github/ccoVeille/go-safecast)

## Project purpose
go-safecast solves the type conversion issues in Go

In Go, integer type conversion can lead to a silent and unexpected behavior and errors if not handled carefully.

This package is made to help to convert any number to another, and report an error when if there would be a [loss or overflow in the conversion](#conversion-overflows)
This package helps to convert any number to another, and report an error when if there would be a [loss or overflow in the conversion](#conversion-issues)

## Usage

Expand All @@ -24,42 +24,43 @@ import (
)

func main() {
var a int

// when there is no overflow
//
fmt.Println(safecast.ToInt8(float64(42)))
// Output: 42, nil
fmt.Println(safecast.ToInt8(int64(-1)))
// Output: -1, nil

// when there is an overflow
//
fmt.Println(safecast.ToInt8(float64(20000)))
// Output: 0 conversion issue: 20000 is greater than 127
fmt.Println(safecast.ToUint8(int64(-1)))
// Output: 0 conversion issue: -1 is negative
fmt.Println(safecast.ToInt16(int32(40000)))
// Output: 0 conversion issue: 40000 is greater than 32767
fmt.Println(safecast.ToUint16(int64(-1)))
// Output: 0 conversion issue: -1 is negative
fmt.Println(safecast.ToInt32(math.MaxUint32 + 1))
// Output: 0 conversion issue: 4294967296 is greater than 2147483647
fmt.Println(safecast.ToUint32(int64(-1)))
// Output: 0 conversion issue: -1 is negative
fmt.Println(safecast.ToInt64(uint64(math.MaxInt64) + 1))
// Output: 0 conversion issue: 9223372036854775808 is greater than 9223372036854775807
fmt.Println(safecast.ToUint64(int8(-1)))
// Output: 0 conversion issue: -1 is negative
fmt.Println(safecast.ToInt(uint64(math.MaxInt) + 1))
// Output: 0 conversion issue: 9223372036854775808 is greater than 9223372036854775807
fmt.Println(safecast.ToUint(int8(-1)))
// Output: 0 conversion issue: -1 is negative
a = 42
b, err := safecast.ToUint8(a) // everything is fine
if err != nil {
fmt.Println(err)
}
fmt.Println(b)
// Output: 42

a = 255 + 1
_, err = safecast.ToUint8(a) // 256 is greater than uint8 maximum value
if err != nil {
fmt.Println(err)
// Output: conversion issue: 256 (int) is greater than 255 (uint8): maximum value for this type exceeded
}

a = -1
_, err = safecast.ToUint8(a) // -1 cannot fit in uint8
if err != nil {
fmt.Println(err)
// Output: conversion issue: -1 (int) is less than 0 (uint8): minimum value for this type exceeded
}

str := "\x99" // ASCII code 153 for Trademark symbol
e := str[0]
_, err = safecast.ToInt8(e)
if err != nil {
fmt.Println(err)
// Output: conversion issue: 153 (uint8) is greater than 127 (int8): maximum value for this type exceeded
}
}
```

[Go Playground](https://go.dev/play/p/VCrv1aLJjMQ)
[Go Playground](https://go.dev/play/p/nelJshulOnj)

## Conversion overflows
## Conversion issues

Issues can happen when converting between signed and unsigned integers, or when converting to a smaller integer type.

Expand Down Expand Up @@ -90,6 +91,45 @@ func main() {

[Go Playground](https://go.dev/play/p/DHfNUcZBvVn)

So you need to adapt your code to write something like this.

```go
package main

import "fmt"

func main() {
var a int64
a = 42
if a < 0 || a > math.MaxUint8 {
log.Println("overflow") // Output: overflow
}
fmt.Println(b) // 42

a = 255 // this is the math.MaxUint8
b = uint8(a)
fmt.Println(b) // 255

a = 255 + 1
b = uint8(a)
if a < 0 || a > math.MaxUint8 {
log.Println("overflow") // Output: overflow
}
fmt.Println(b) // Output: 0

a = -1
b = uint8(a)
if a < 0 || a > math.MaxUint8 {
log.Println("overflow") // Output: overflow
}
fmt.Println(b) // Output:255
}
```

[Go Playground](https://go.dev/play/p/qAHGyy4NCLP)

`go-safecast` is there to avoid boilerplate copy pasta.

## Motivation

The gosec project raised this to my attention when the gosec [G115 rule was added](https://github.com/securego/gosec/pull/1149)
Expand Down
2 changes: 1 addition & 1 deletion conversion.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Package safecast aims to solve the issue of type conversion
// Package go-safecast solves the type conversion issues in Go
//
// In Go, integer type conversion can lead to unexpected behavior and errors if not handled carefully.
// Issues can happen when converting between signed and unsigned integers, or when converting to a smaller integer type.
Expand Down
188 changes: 188 additions & 0 deletions examples_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package safecast_test

import (
"fmt"
"math"

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

func ExampleToInt8() {
a := float64(42.42)
i, err := safecast.ToInt8(a)
if err != nil {
fmt.Println(err)
}
fmt.Println(i)

a = float64(200.42)
_, err = safecast.ToInt8(a)
if err != nil {
fmt.Println(err)
}
// Output:
// 42
// conversion issue: 200.42 (float64) is greater than 127 (int8): maximum value for this type exceeded
}

func ExampleToUint8() {
a := int64(42)
i, err := safecast.ToUint8(a)
if err != nil {
fmt.Println(err)
}
fmt.Println(i)

a = int64(-1)
_, err = safecast.ToUint8(a)
if err != nil {
fmt.Println(err)
}
// Output:
// 42
// conversion issue: -1 (int64) is less than 0 (uint8): minimum value for this type exceeded
}

func ExampleToInt16() {
a := int32(42)
i, err := safecast.ToInt16(a)
if err != nil {
fmt.Println(err)
}
fmt.Println(i)

a = int32(40000)
_, err = safecast.ToInt16(a)
if err != nil {
fmt.Println(err)
}
// Output:
// 42
// conversion issue: 40000 (int32) is greater than 32767 (int16): maximum value for this type exceeded
}

func ExampleToUint16() {
a := int64(42)
i, err := safecast.ToUint16(a)
if err != nil {
fmt.Println(err)
}
fmt.Println(i)

a = int64(-1)
_, err = safecast.ToUint16(a)
if err != nil {
fmt.Println(err)
}
// Output:
// 42
// conversion issue: -1 (int64) is less than 0 (uint16): minimum value for this type exceeded
}

func ExampleToInt32() {
a := int(42)
i, err := safecast.ToInt32(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
}

func ExampleToUint32() {
a := int16(42)
i, err := safecast.ToUint32(a)
if err != nil {
fmt.Println(err)
}
fmt.Println(i)

a = int16(-1)
_, err = safecast.ToUint32(a)
if err != nil {
fmt.Println(err)
}
// Output:
// 42
// conversion issue: -1 (int16) is less than 0 (uint32): minimum value for this type exceeded
}

func ExampleToInt64() {
a := uint64(42)
i, err := safecast.ToInt64(a)
if err != nil {
fmt.Println(err)
}
fmt.Println(i)

a = uint64(math.MaxInt64) + 1
_, err = safecast.ToInt64(a)
if err != nil {
fmt.Println(err)
}
// Output:
// 42
// conversion issue: 9223372036854775808 (uint64) is greater than 9223372036854775807 (int64): maximum value for this type exceeded
}

func ExampleToUint64() {
a := int8(42)
i, err := safecast.ToUint64(a)
if err != nil {
fmt.Println(err)
}
fmt.Println(i)

a = int8(-1)
_, err = safecast.ToUint64(a)
if err != nil {
fmt.Println(err)
}
// Output:
// 42
// conversion issue: -1 (int8) is less than 0 (uint64): minimum value for this type exceeded
}

func ExampleToInt() {
a := uint64(42)
i, err := safecast.ToInt(a)
if err != nil {
fmt.Println(err)
}
fmt.Println(i)

a = uint64(math.MaxInt64) + 1
_, err = safecast.ToInt(a)
if err != nil {
fmt.Println(err)
}
// Output:
// 42
// conversion issue: 9223372036854775808 (uint64) is greater than 9223372036854775807 (int): maximum value for this type exceeded
}

func ExampleToUint() {
a := int8(42)
i, err := safecast.ToUint(a)
if err != nil {
fmt.Println(err)
}
fmt.Println(i)

a = int8(-1)
_, err = safecast.ToUint(a)
if err != nil {
fmt.Println(err)
}
// Output:
// 42
// conversion issue: -1 (int8) is less than 0 (uint): minimum value for this type exceeded
}
43 changes: 43 additions & 0 deletions readme_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package safecast_test

import (
"fmt"

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

func Example() {
var a int

a = 42
b, err := safecast.ToUint8(a) // everything is fine
if err != nil {
fmt.Println(err)
}
fmt.Println(b)

a = 255 + 1
_, err = safecast.ToUint8(a) // 256 is greater than uint8 maximum value
if err != nil {
fmt.Println(err)
}

a = -1
_, err = safecast.ToUint8(a) // -1 cannot fit in uint8
if err != nil {
fmt.Println(err)
}

str := "\x99" // ASCII code 153 for Trademark symbol
e := str[0]
_, err = safecast.ToInt8(e)
if err != nil {
fmt.Println(err)
}

// Output:
// 42
// conversion issue: 256 (int) is greater than 255 (uint8): maximum value for this type exceeded
// conversion issue: -1 (int) is less than 0 (uint8): minimum value for this type exceeded
// conversion issue: 153 (uint8) is greater than 127 (int8): maximum value for this type exceeded
}

0 comments on commit 618c455

Please sign in to comment.