1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-08-10 22:31:32 +02:00

fix: add json serialization support for option

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
Dr. Carsten Leue
2023-07-09 10:21:56 +02:00
parent c169cb42bb
commit 691ceb0675
4 changed files with 86 additions and 52 deletions

View File

@@ -2,29 +2,21 @@ package either
import "fmt"
type EitherTag string
const (
leftTag = "Left"
rightTag = "Right"
)
// Either defines a data structure that logically holds either an E or an A. The tag discriminates the cases
type Either[E, A any] struct {
tag EitherTag `default:"Left"`
// Either defines a data structure that logically holds either an E or an A. The flag discriminates the cases
type (
Either[E, A any] struct {
isLeft bool
left E
right A
}
}
)
// String prints some debug info for the object
func (s Either[E, A]) String() string {
switch s.tag {
case leftTag:
return fmt.Sprintf("%s[%T, %T](%v)", s.tag, s.left, s.right, s.left)
case rightTag:
return fmt.Sprintf("%s[%T, %T](%v)", s.tag, s.left, s.right, s.right)
if s.isLeft {
return fmt.Sprintf("Left[%T, %T](%v)", s.left, s.right, s.left)
}
return "Invalid"
return fmt.Sprintf("Right[%T, %T](%v)", s.left, s.right, s.right)
}
// Format prints some debug info for the object
@@ -38,19 +30,19 @@ func (s Either[E, A]) Format(f fmt.State, c rune) {
}
func IsLeft[E, A any](val Either[E, A]) bool {
return val.tag == leftTag
return val.isLeft
}
func IsRight[E, A any](val Either[E, A]) bool {
return val.tag == rightTag
return !val.isLeft
}
func Left[E, A any](value E) Either[E, A] {
return Either[E, A]{tag: leftTag, left: value}
return Either[E, A]{isLeft: true, left: value}
}
func Right[E, A any](value A) Either[E, A] {
return Either[E, A]{tag: rightTag, right: value}
return Either[E, A]{isLeft: false, right: value}
}
func MonadFold[E, A, B any](ma Either[E, A], onLeft func(e E) B, onRight func(a A) B) B {

View File

@@ -11,6 +11,12 @@ import (
"github.com/stretchr/testify/assert"
)
func TestDefault(t *testing.T) {
var e Either[error, string]
assert.Equal(t, Of[error](""), e)
}
func TestIsLeft(t *testing.T) {
err := errors.New("Some error")
withError := Left[error, string](err)

View File

@@ -6,32 +6,22 @@ import (
"fmt"
)
type OptionTag string
const (
noneTag = "None"
someTag = "Some"
)
var (
jsonNull = []byte("null")
)
// Option defines a data structure that logically holds a value or not
type Option[A any] struct {
tag OptionTag `default:"None"`
value A
isSome bool
some A
}
// String prints some debug info for the object
func (s Option[A]) String() string {
switch s.tag {
case noneTag:
return fmt.Sprintf("%s[%T]", s.tag, s.value)
case someTag:
return fmt.Sprintf("%s[%T](%v)", s.tag, s.value, s.value)
if s.isSome {
return fmt.Sprintf("Some[%T](%v)", s.some, s.some)
}
return "Invalid"
return fmt.Sprintf("None[%T]", s.some)
}
// Format prints some debug info for the object
@@ -45,28 +35,29 @@ func (s Option[A]) Format(f fmt.State, c rune) {
}
func (s Option[A]) MarshalJSON() ([]byte, error) {
if s.tag == noneTag {
return jsonNull, nil
if IsSome(s) {
return json.Marshal(s.some)
}
return json.Marshal(s.value)
return jsonNull, nil
}
func (s Option[A]) UnmarshalJSON(data []byte) error {
func (s *Option[A]) UnmarshalJSON(data []byte) error {
// decode the value
if bytes.Equal(data, jsonNull) {
s.tag = noneTag
s.isSome = false
s.some = *new(A)
return nil
}
s.tag = someTag
return json.Unmarshal(data, &s.value)
s.isSome = true
return json.Unmarshal(data, &s.some)
}
func IsNone[T any](val Option[T]) bool {
return val.tag == noneTag
return !val.isSome
}
func Some[T any](value T) Option[T] {
return Option[T]{tag: someTag, value: value}
return Option[T]{isSome: true, some: value}
}
func Of[T any](value T) Option[T] {
@@ -74,20 +65,20 @@ func Of[T any](value T) Option[T] {
}
func None[T any]() Option[T] {
return Option[T]{tag: noneTag}
return Option[T]{isSome: false}
}
func IsSome[T any](val Option[T]) bool {
return val.tag == someTag
return val.isSome
}
func MonadFold[A, B any](ma Option[A], onNone func() B, onSome func(A) B) B {
if IsNone(ma) {
return onNone()
if IsSome(ma) {
return onSome(ma.some)
}
return onSome(ma.value)
return onNone()
}
func Unwrap[A any](ma Option[A]) (A, bool) {
return ma.value, ma.tag == someTag
return ma.some, ma.isSome
}

View File

@@ -1,14 +1,59 @@
package option
import (
"encoding/json"
"fmt"
"testing"
F "github.com/ibm/fp-go/function"
"github.com/ibm/fp-go/internal/utils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type (
SampleData struct {
Value string
OptValue Option[string]
}
)
func TestJson(t *testing.T) {
sample := SampleData{
Value: "value",
OptValue: Of("optValue"),
}
data, err := json.Marshal(&sample)
require.NoError(t, err)
var deser SampleData
err = json.Unmarshal(data, &deser)
require.NoError(t, err)
assert.Equal(t, sample, deser)
sample = SampleData{
Value: "value",
OptValue: None[string](),
}
data, err = json.Marshal(&sample)
require.NoError(t, err)
err = json.Unmarshal(data, &deser)
require.NoError(t, err)
assert.Equal(t, sample, deser)
}
func TestDefault(t *testing.T) {
var e Option[string]
assert.Equal(t, None[string](), e)
}
func TestReduce(t *testing.T) {
assert.Equal(t, 2, F.Pipe1(None[int](), Reduce(utils.Sum, 2)))