Skip to content

Commit

Permalink
merge
Browse files Browse the repository at this point in the history
  • Loading branch information
samber committed Sep 21, 2022
2 parents 9c9517b + 89682f5 commit 1a1613d
Show file tree
Hide file tree
Showing 4 changed files with 418 additions and 0 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,16 @@ Methods:
- `.Map()` [doc](https://pkg.go.dev/github.com/samber/mo#Option.Map)
- `.MapNone()` [doc](https://pkg.go.dev/github.com/samber/mo#Option.MapNone)
- `.FlatMap()` [doc](https://pkg.go.dev/github.com/samber/mo#Option.FlatMap)
- `.MarshalJSON()` [doc](https://pkg.go.dev/github.com/samber/mo#Option.MarshalJSON)
- `.UnmarshalJSON()` [doc](https://pkg.go.dev/github.com/samber/mo#Option.UnmarshalJSON)
- `.MarshalText()` [doc](https://pkg.go.dev/github.com/samber/mo#Option.MarshalText)
- `.UnmarshalText()` [doc](https://pkg.go.dev/github.com/samber/mo#Option.UnmarshalText)
- `.MarshalBinary()` [doc](https://pkg.go.dev/github.com/samber/mo#Option.MarshalBinary)
- `.UnmarshalBinary()` [doc](https://pkg.go.dev/github.com/samber/mo#Option.UnmarshalBinary)
- `.GobEncode()` [doc](https://pkg.go.dev/github.com/samber/mo#Option.GobEncode)
- `.GobDecode()` [doc](https://pkg.go.dev/github.com/samber/mo#Option.GobDecode)
- `.Scan()` [doc](https://pkg.go.dev/github.com/samber/mo#Option.Scan)
- `.Value()` [doc](https://pkg.go.dev/github.com/samber/mo#Option.Value)

### Result[T any]

Expand Down
118 changes: 118 additions & 0 deletions option.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package mo

import (
"bytes"
"database/sql/driver"
"encoding/gob"
"encoding/json"
"errors"
"fmt"
"reflect"
"time"
)

var optionNoSuchElement = fmt.Errorf("no such element")
Expand Down Expand Up @@ -141,3 +147,115 @@ func (o Option[T]) FlatMap(mapper func(value T) Option[T]) Option[T] {

return None[T]()
}

// MarshalJSON encodes Option into json.
func (o Option[T]) MarshalJSON() ([]byte, error) {
if o.isPresent {
return json.Marshal(o.value)
}
return json.Marshal(nil)
}

// UnmarshalJSON decodes Option from json.
func (o *Option[T]) UnmarshalJSON(b []byte) error {
if bytes.Equal(b, []byte("null")) {
o.isPresent = false
return nil
}

err := json.Unmarshal(b, &o.value)
if err != nil {
return err
}

o.isPresent = true
time.Now()
return nil
}

// MarshalText implements the encoding.TextMarshaler interface.
func (o Option[T]) MarshalText() ([]byte, error) {
return json.Marshal(o)
}

// UnmarshalText implements the encoding.TextUnmarshaler interface.
func (o *Option[T]) UnmarshalText(data []byte) error {
return json.Unmarshal(data, o)
}

// BinaryMarshaler is the interface implemented by an object that can marshal itself into a binary form.
func (o Option[T]) MarshalBinary() ([]byte, error) {
if !o.isPresent {
return []byte{0}, nil
}

var buf bytes.Buffer

enc := gob.NewEncoder(&buf)
if err := enc.Encode(o.value); err != nil {
return []byte{}, err
}

return append([]byte{1}, buf.Bytes()...), nil
}

// BinaryUnmarshaler is the interface implemented by an object that can unmarshal a binary representation of itself.
func (o *Option[T]) UnmarshalBinary(data []byte) error {
if len(data) == 0 {
return errors.New("Option[T].UnmarshalBinary: no data")
}

if data[0] == 0 {
o.isPresent = false
o.value = empty[T]()
return nil
}

buf := bytes.NewBuffer(data[1:])
dec := gob.NewDecoder(buf)
err := dec.Decode(&o.value)
if err != nil {
return err
}

o.isPresent = true
return nil
}

// GobEncode implements the gob.GobEncoder interface.
func (o Option[T]) GobEncode() ([]byte, error) {
return o.MarshalBinary()
}

// GobDecode implements the gob.GobDecoder interface.
func (o *Option[T]) GobDecode(data []byte) error {
return o.UnmarshalBinary(data)
}

// Scan implements the SQL driver.Scanner interface.
func (o *Option[T]) Scan(src any) error {
if src == nil {
o.isPresent = false
o.value = empty[T]()
return nil
}

if av, err := driver.DefaultParameterConverter.ConvertValue(src); err == nil {
if v, ok := av.(T); ok {
o.isPresent = true
o.value = v
return nil
}
}

return fmt.Errorf("failed to scan Option[T]")
}

// Value implements the driver Valuer interface.
func (o Option[T]) Value() (driver.Value, error) {
if !o.isPresent {
return nil, nil
}

return o.value, nil
}
65 changes: 65 additions & 0 deletions option_example_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package mo

import (
"encoding/json"
"fmt"
)

Expand Down Expand Up @@ -280,3 +281,67 @@ func ExampleOption_FlatMap_none() {
fmt.Println(result.IsPresent(), result.OrEmpty())
// Output: false 0
}

func ExampleOption_MarshalJSON_some() {
type test struct {
Email Option[string] `json:"email"`
}

value := test{Email: Some("samuel@example.com")}
result, err := json.Marshal(value)

fmt.Println(string(result))
fmt.Println(err)
// Output:
// {"email":"samuel@example.com"}
// <nil>
}

func ExampleOption_MarshalJSON_none() {
type test struct {
Email Option[string] `json:"email"`
}

value := test{Email: None[string]()}
result, err := json.Marshal(value)

fmt.Println(string(result))
fmt.Println(err)
// Output:
// {"email":null}
// <nil>
}

func ExampleOption_UnmarshalJSON_some() {
type test struct {
Email Option[string] `json:"email"`
}

value := []byte(`{"email":"samuel@example.com"}`)

var result test
err := json.Unmarshal(value, &result)

fmt.Println(result.Email.Get())
fmt.Println(err)
// Output:
// samuel@example.com true
// <nil>
}

func ExampleOption_UnmarshalJSON_none() {
type test struct {
Email Option[string] `json:"email"`
}

value := []byte(`{"email":null}`)

var result test
err := json.Unmarshal(value, &result)

fmt.Println(result.Email.Get())
fmt.Println(err)
// Output:
// false
// <nil>
}

0 comments on commit 1a1613d

Please sign in to comment.