Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [5.25.0] - 2024-01-22
### Added
- Add additional `Option.Scan` type support for `sql.Scanner` interface of Uint, Uint16, Uint32, Uint64, Int, Int, Int8, Float32, []byte, json.RawValue.

## [5.24.0] - 2024-01-21
### Added
- `appext` package for application level helpers. Specifically added setting up os signal trapping and cancellation of context.Context.
Expand Down Expand Up @@ -90,7 +94,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Added `timext.NanoTime` for fast low level monotonic time with nanosecond precision.

[Unreleased]: https://github.com/go-playground/pkg/compare/v5.23.0...HEAD
[Unreleased]: https://github.com/go-playground/pkg/compare/v5.25.0...HEAD
[5.25.0]: https://github.com/go-playground/pkg/compare/v5.24.0..v5.25.0
[5.24.0]: https://github.com/go-playground/pkg/compare/v5.23.0..v5.24.0
[5.23.0]: https://github.com/go-playground/pkg/compare/v5.22.0..v5.23.0
[5.22.0]: https://github.com/go-playground/pkg/compare/v5.21.3..v5.22.0
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# pkg

![Project status](https://img.shields.io/badge/version-5.24.0-green.svg)
![Project status](https://img.shields.io/badge/version-5.25.0-green.svg)
[![Lint & Test](https://github.com/go-playground/pkg/actions/workflows/go.yml/badge.svg)](https://github.com/go-playground/pkg/actions/workflows/go.yml)
[![Coverage Status](https://coveralls.io/repos/github/go-playground/pkg/badge.svg?branch=master)](https://coveralls.io/github/go-playground/pkg?branch=master)
[![GoDoc](https://godoc.org/github.com/go-playground/pkg?status.svg)](https://pkg.go.dev/mod/github.com/go-playground/pkg/v5)
Expand Down
40 changes: 38 additions & 2 deletions values/option/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"database/sql/driver"
"encoding/json"
"fmt"
"math"
"reflect"
"time"
)
Expand Down Expand Up @@ -225,12 +226,43 @@ func (o *Option[T]) Scan(value any) error {
return err
}
*o = Some(reflect.ValueOf(v.Byte).Convert(val.Type()).Interface().(T))
case reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
v := reflect.ValueOf(value)
if v.Type().ConvertibleTo(val.Type()) {
*o = Some(reflect.ValueOf(v.Convert(val.Type()).Interface()).Interface().(T))
} else {
return fmt.Errorf("value %T not convertable to %T", value, o.value)
}
case reflect.Float32:
var v sql.NullFloat64
if err := v.Scan(value); err != nil {
return err
}
*o = Some(reflect.ValueOf(v.Float64).Convert(val.Type()).Interface().(T))
case reflect.Float64:
var v sql.NullFloat64
if err := v.Scan(value); err != nil {
return err
}
*o = Some(reflect.ValueOf(v.Float64).Convert(val.Type()).Interface().(T))
case reflect.Int:
var v sql.NullInt64
if err := v.Scan(value); err != nil {
return err
}
if v.Int64 > math.MaxInt || v.Int64 < math.MinInt {
return fmt.Errorf("value %d out of range for int", v.Int64)
}
*o = Some(reflect.ValueOf(v.Int64).Convert(val.Type()).Interface().(T))
case reflect.Int8:
var v sql.NullInt64
if err := v.Scan(value); err != nil {
return err
}
if v.Int64 > math.MaxInt8 || v.Int64 < math.MinInt8 {
return fmt.Errorf("value %d out of range for int8", v.Int64)
}
*o = Some(reflect.ValueOf(v.Int64).Convert(val.Type()).Interface().(T))
case reflect.Int16:
var v sql.NullInt16
if err := v.Scan(value); err != nil {
Expand Down Expand Up @@ -285,8 +317,12 @@ func (o *Option[T]) Scan(value any) error {
v := reflect.ValueOf(value)

if v.Type().ConvertibleTo(byteSliceType) {
if err := json.Unmarshal(v.Convert(byteSliceType).Interface().([]byte), &o.value); err != nil {
return err
if val.Kind() == reflect.Slice && val.Type().Elem().Kind() == reflect.Uint8 {
*o = Some(reflect.ValueOf(v.Convert(val.Type()).Interface()).Interface().(T))
} else {
if err := json.Unmarshal(v.Convert(byteSliceType).Interface().([]byte), &o.value); err != nil {
return err
}
}
o.isSome = true
return nil
Expand Down
70 changes: 70 additions & 0 deletions values/option/option_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package optionext
import (
"database/sql/driver"
"encoding/json"
"math"
"reflect"
"testing"
"time"
Expand Down Expand Up @@ -222,12 +223,22 @@ func TestSQLScanner(t *testing.T) {
var optionI64 Option[int64]
var optionI32 Option[int32]
var optionI16 Option[int16]
var optionI8 Option[int8]
var optionI Option[int]
var optionString Option[string]
var optionBool Option[bool]
var optionF32 Option[float32]
var optionF64 Option[float64]
var optionByte Option[byte]
var optionTime Option[time.Time]
var optionInterface Option[any]
var optionArrBytes Option[[]byte]
var optionRawMessage Option[json.RawMessage]
var optionUint64 Option[uint64]
var optionUint32 Option[uint32]
var optionUint16 Option[uint16]
var optionUint8 Option[uint8]
var optionUint Option[uint]

err := optionInterface.Scan(1)
Equal(t, err, nil)
Expand All @@ -237,6 +248,29 @@ func TestSQLScanner(t *testing.T) {
Equal(t, err, nil)
Equal(t, optionInterface, Some(any("blah")))

err = optionUint64.Scan(uint64(200))
Equal(t, err, nil)
Equal(t, optionUint64, Some(uint64(200)))

err = optionUint32.Scan(uint32(200))
Equal(t, err, nil)
Equal(t, optionUint32, Some(uint32(200)))

err = optionUint16.Scan(uint16(200))
Equal(t, err, nil)
Equal(t, optionUint16, Some(uint16(200)))

err = optionUint8.Scan(uint8(200))
Equal(t, err, nil)
Equal(t, optionUint8, Some(uint8(200)))

err = optionUint.Scan(uint(200))
Equal(t, err, nil)
Equal(t, optionUint, Some(uint(200)))

err = optionUint64.Scan("200")
Equal(t, err.Error(), "value string not convertable to uint64")

err = optionI64.Scan(value)
Equal(t, err, nil)
Equal(t, optionI64, Some(value))
Expand All @@ -249,6 +283,18 @@ func TestSQLScanner(t *testing.T) {
Equal(t, err, nil)
Equal(t, optionI16, Some(int16(value)))

err = optionI8.Scan(math.MaxInt32)
Equal(t, err.Error(), "value 2147483647 out of range for int8")
Equal(t, optionI8, None[int8]())

err = optionI8.Scan(int8(3))
Equal(t, err, nil)
Equal(t, optionI8, Some(int8(3)))

err = optionI.Scan(3)
Equal(t, err, nil)
Equal(t, optionI, Some(3))

err = optionBool.Scan(1)
Equal(t, err, nil)
Equal(t, optionBool, Some(true))
Expand All @@ -257,6 +303,14 @@ func TestSQLScanner(t *testing.T) {
Equal(t, err, nil)
Equal(t, optionString, Some("123"))

err = optionF32.Scan(float32(2.0))
Equal(t, err, nil)
Equal(t, optionF32, Some(float32(2.0)))

err = optionF32.Scan(math.MaxFloat64)
Equal(t, err, nil)
Equal(t, optionF32, Some(float32(math.Inf(1))))

err = optionF64.Scan(2.0)
Equal(t, err, nil)
Equal(t, optionF64, Some(2.0))
Expand Down Expand Up @@ -324,6 +378,22 @@ func TestSQLScanner(t *testing.T) {
err = ct.Scan("test")
Equal(t, err, nil)
Equal(t, ct, Some(customStringType("test")))

err = optionArrBytes.Scan([]byte(`[1,2,3]`))
Equal(t, err, nil)
Equal(t, optionArrBytes, Some([]byte(`[1,2,3]`)))

err = optionArrBytes.Scan([]byte{4, 5, 6})
Equal(t, err, nil)
Equal(t, optionArrBytes, Some([]byte{4, 5, 6}))

err = optionRawMessage.Scan([]byte(`[1,2,3]`))
Equal(t, err, nil)
Equal(t, true, string(optionRawMessage.Unwrap()) == "[1,2,3]")

err = optionRawMessage.Scan([]byte{4, 5, 6})
Equal(t, err, nil)
Equal(t, true, string(optionRawMessage.Unwrap()) == string([]byte{4, 5, 6}))
}

func TestNilOption(t *testing.T) {
Expand Down