diff --git a/option.go b/option.go index 0250b23..839fa8c 100644 --- a/option.go +++ b/option.go @@ -1,5 +1,7 @@ package immutable +import "encoding/json" + // Option represents an item that may or may not have a value. type Option[T any] struct { // If HasValue is true, this Option contains a value, if @@ -23,7 +25,7 @@ func None[T any]() Option[T] { return Option[T]{} } -// HasValue returns a boolean indicating whether or not this optino contains a value. If +// HasValue returns a boolean indicating whether or not this option contains a value. If // it returns true, this Option contains a value, if it is false it contains no value. func (o Option[T]) HasValue() bool { return o.hasValue @@ -34,3 +36,26 @@ func (o Option[T]) HasValue() bool { func (o Option[T]) Value() T { return o.value } + +// MarshalJSON implements the json.Marshaler interface. +func (o Option[T]) MarshalJSON() ([]byte, error) { + if o.HasValue() { + return json.Marshal(o.Value()) + } + return []byte("null"), nil +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (o *Option[T]) UnmarshalJSON(b []byte) error { + if string(b) == "null" { + *o = None[T]() + return nil + } + var value T + err := json.Unmarshal(b, &value) + if err != nil { + return err + } + *o = Some(value) + return nil +} diff --git a/option_test.go b/option_test.go new file mode 100644 index 0000000..26d8570 --- /dev/null +++ b/option_test.go @@ -0,0 +1,70 @@ +package immutable + +import ( + "encoding/json" + "testing" +) + +func TestSome(t *testing.T) { + opt := Some(1) + if !opt.HasValue() { + t.Errorf("expected Some to return an Option with a value") + } + if opt.Value() != 1 { + t.Errorf("expected Some to return an Option with a value of 1") + } +} + +func TestNone(t *testing.T) { + opt := None[int]() + if opt.HasValue() { + t.Errorf("expected None to return an Option with no value") + } +} + +func TestOptionMarshal(t *testing.T) { + opt := Some(1) + b, err := json.Marshal(opt) + if err != nil { + t.Errorf("expected no error, got %v", err) + } + if string(b) != "1" { + t.Errorf("expected 1, got %s", b) + } +} + +func TestOptionMarshalNone(t *testing.T) { + opt := None[int]() + b, err := json.Marshal(opt) + if err != nil { + t.Errorf("expected no error, got %v", err) + } + if string(b) != "null" { + t.Errorf("expected null, got %s", b) + } +} + +func TestOptionUnmarshal(t *testing.T) { + var opt Option[int] + err := json.Unmarshal([]byte("1"), &opt) + if err != nil { + t.Errorf("expected no error, got %v", err) + } + if !opt.HasValue() { + t.Errorf("expected Some to return an Option with a value") + } + if opt.Value() != 1 { + t.Errorf("expected Some to return an Option with a value of 1") + } +} + +func TestOptionUnmarshalNone(t *testing.T) { + var opt Option[int] + err := json.Unmarshal([]byte("null"), &opt) + if err != nil { + t.Errorf("expected no error, got %v", err) + } + if opt.HasValue() { + t.Errorf("expected None to return an Option with no value") + } +}