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
58 changes: 28 additions & 30 deletions safetypes/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,25 @@ import (
)

// None creates a None variant of Option.
func None[T any]() *Option[T] {
func None[T any]() Option[T] {
var v T

return &Option[T]{
return Option[T]{
ok: false,
val: v,
}
}

// Some creates a Some variant of Option from the value.
func Some[T any](val T) *Option[T] {
return &Option[T]{
func Some[T any](val T) Option[T] {
return Option[T]{
ok: true,
val: val,
}
}

// OptionOf creates an Option from the given value.
func OptionOf[T any](val T) *Option[T] {
func OptionOf[T any](val T) Option[T] {
if reflect.ValueOf(&val).Elem().IsZero() {
return None[T]()
}
Expand All @@ -37,7 +37,7 @@ func OptionOf[T any](val T) *Option[T] {
// (if None).
//
// With Go 1.27+, use Option[T].Map().
func MapOption[T any, U any](opt *Option[T], f func(T) U) *Option[U] {
func MapOption[T any, U any](opt Option[T], f func(T) U) Option[U] {
if !opt.ok {
return None[U]()
}
Expand All @@ -48,7 +48,7 @@ func MapOption[T any, U any](opt *Option[T], f func(T) U) *Option[U] {
// MapOptionOr returns the provided default result (if None), or applies a function to the contained value (if Some).
//
// With Go 1.27+, use Option[T].MapOr().
func MapOptionOr[T any, U any](opt *Option[T], def U, f func(T) U) U {
func MapOptionOr[T any, U any](opt Option[T], def U, f func(T) U) U {
if !opt.ok {
return def
}
Expand All @@ -60,7 +60,7 @@ func MapOptionOr[T any, U any](opt *Option[T], def U, f func(T) U) U {
// (if Some).
//
// With Go 1.27+, use Option[T].MapOrElse().
func MapOptionOrElse[T any, U any](opt *Option[T], factory func() U, f func(T) U) U {
func MapOptionOrElse[T any, U any](opt Option[T], factory func() U, f func(T) U) U {
if !opt.ok {
return factory()
}
Expand All @@ -71,7 +71,7 @@ func MapOptionOrElse[T any, U any](opt *Option[T], factory func() U, f func(T) U
// And returns None if the Option is None, otherwise returns `optb`.
//
// With Go 1.27+, use Option[T].And().
func And[T any, U any](opt *Option[T], other *Option[U]) *Option[U] {
func And[T any, U any](opt Option[T], other Option[U]) Option[U] {
if !opt.ok {
return None[U]()
}
Expand All @@ -82,7 +82,7 @@ func And[T any, U any](opt *Option[T], other *Option[U]) *Option[U] {
// AndThen returns None if the Option is None, otherwise calls `f` with the wrapped value and returns the result.
//
// With Go 1.27+, use Option[T].AndThen().
func AndThen[T any, U any](opt *Option[T], f func(T) *Option[U]) *Option[U] {
func AndThen[T any, U any](opt Option[T], f func(T) Option[U]) Option[U] {
if !opt.ok {
return None[U]()
}
Expand Down Expand Up @@ -172,7 +172,7 @@ func (o *Option[T]) UnwrapOrDefault() T {
}

// AsOkOr converts an Option to an Ok when opt is Some or Err when opt is None.
func (o *Option[T]) AsOkOr(err error) *Result[T] {
func (o *Option[T]) AsOkOr(err error) Result[T] {
if o.ok {
return Ok[T](o.val)
}
Expand All @@ -181,7 +181,7 @@ func (o *Option[T]) AsOkOr(err error) *Result[T] {
}

// AsOkOrElse converts an Option to an Ok when opt is Some or Err when opt is None.
func (o *Option[T]) AsOkOrElse(f func() error) *Result[T] {
func (o *Option[T]) AsOkOrElse(f func() error) Result[T] {
if o.ok {
return Ok[T](o.val)
}
Expand All @@ -190,51 +190,51 @@ func (o *Option[T]) AsOkOrElse(f func() error) *Result[T] {
}

// Inspect calls a function with a reference to the contained value if Some. Returns the original Option.
func (o *Option[T]) Inspect(f func(T)) *Option[T] {
func (o *Option[T]) Inspect(f func(T)) Option[T] {
if o.ok {
f(o.val)
}

return o
return *o
}

// Filter returns None if the Option is None, otherwise calls predicate with the wrapped value and returns:
// - Some(t) if the predicate returns true (where t is the wrapped value), and
// - None if the predicate returns false.
func (o *Option[T]) Filter(f func(T) bool) *Option[T] {
func (o *Option[T]) Filter(f func(T) bool) Option[T] {
if o.ok && f(o.val) {
return o
return *o
}

return None[T]()
}

// Or returns the Option if it contains a value, otherwise returns optb.
func (o *Option[T]) Or(other *Option[T]) *Option[T] {
func (o *Option[T]) Or(other Option[T]) Option[T] {
if o.ok {
return o
return *o
}

return other
}

// OrElse returns the option if it contains a value, otherwise calls f and returns the result.
func (o *Option[T]) OrElse(f func() *Option[T]) *Option[T] {
func (o *Option[T]) OrElse(f func() Option[T]) Option[T] {
if o.ok {
return o
return *o
}

return f()
}

// Xor returns Some if exactly one of self, optb is Some, otherwise returns None.
func (o *Option[T]) Xor(other *Option[T]) *Option[T] {
func (o *Option[T]) Xor(other Option[T]) Option[T] {
if o.ok == other.ok {
return None[T]()
}

if o.ok {
return o
return *o
}

return other
Expand Down Expand Up @@ -295,30 +295,28 @@ func (o *Option[T]) GetOrInsertWith(f func() T) *T {
}

// Take takes the value out of the Option, leaving a None in its place.
func (o *Option[T]) Take() *Option[T] {
if o.ok {
res := *o
func (o *Option[T]) Take() Option[T] {
res := *o

if o.ok {
o.ok = false

return &res
}

return o
return res
}

// TakeIf takes the value out of the Option, but only if the predicate evaluates to true on a mutable reference to
// the value.
//
// In other words, replaces self with None if the predicate returns true. This method operates similar to take but
// conditional.
func (o *Option[T]) TakeIf(f func(T) bool) *Option[T] {
func (o *Option[T]) TakeIf(f func(T) bool) Option[T] {
if o.ok && f(o.val) {
res := *o

o.ok = false

return &res
return res
}

return None[T]()
Expand Down
8 changes: 4 additions & 4 deletions safetypes/option1.27.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package st

// Map maps an Option<T> to Option<U> by applying a function to a contained value (if Some) or returns None
// (if None).
func (o *Option[T]) Map[U any](f func(T) U) *Option[U] {
func (o *Option[T]) Map[U any](f func(T) U) Option[U] {
if !o.ok {
return None[U]()
}
Expand Down Expand Up @@ -32,16 +32,16 @@ func (o *Option[T]) MapOrElse[U any](factory func() U, f func(T) U) U {
}

// And returns None if the option is None, otherwise returns `optb`.
func (o *Option[T]) And[U any](other *Option[U]) *Option[U] {
func (o *Option[T]) And[U any](other Option[U]) Option[U] {
if !o.ok {
return None[U]()
}

return other
}

// AndThen returns None if the option is None, otherwise calls `f` with the wrapped value and returns the result.
func (o *Option[T]) AndThen[U any](f func(T) *Option[U]) *Option[U] {
// AndThen returns None if the option is None, otherwise calls `f` with the wrapped value, and returns the result.
func (o *Option[T]) AndThen[U any](f func(T) Option[U]) Option[U] {
if !o.ok {
return None[U]()
}
Expand Down
4 changes: 2 additions & 2 deletions safetypes/option1.27_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,15 @@ func TestOption_AndThen(t *testing.T) {
value := fake.RandomStringWithLength(8)
other := Some(value)

res := o.AndThen(func(int) *Option[string] { return other })
res := o.AndThen(func(int) Option[string] { return other })

assert.Equal(t, other, res)
})

t.Run("none", func(t *testing.T) {
o := None[int]()

f := func(int) *Option[string] {
f := func(int) Option[string] {
assert.Fail(t, "mapper should not have been called")

return Some("")
Expand Down
4 changes: 2 additions & 2 deletions safetypes/option_marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package st
import "encoding/json"

// MarshalJSON implements json.Marshaler.
func (o *Option[T]) MarshalJSON() ([]byte, error) {
if o == nil || !o.ok {
func (o Option[T]) MarshalJSON() ([]byte, error) {
if !o.ok {
return []byte("null"), nil
}

Expand Down
8 changes: 4 additions & 4 deletions safetypes/option_marshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,15 @@ func TestMarshalJSON(t *testing.T) {
struct {
Name string
Job *Option[string]
}{"Poor Schmuck", None[string]()},
}{"Poor Schmuck", ptr(None[string]())},
`{"Name": "Poor Schmuck", "Job": null}`,
},
{
"some inside a struct",
struct {
Name string
Job *Option[string]
}{"Mega Chad", Some("Big Boss")},
}{"Mega Chad", ptr(Some("Big Boss"))},
`{"Name": "Mega Chad", "Job": "Big Boss"}`,
},
}
Expand Down Expand Up @@ -125,7 +125,7 @@ func TestUnmarshalText(t *testing.T) {
err := json.Unmarshal([]byte(`{"Name": "Poor Schmuck", "Job": null}`), &o)
require.NoError(t, err)

expected := Option[S]{ok: true, val: S{"Poor Schmuck", *None[string]()}}
expected := Option[S]{ok: true, val: S{"Poor Schmuck", None[string]()}}
assert.Equal(t, expected, o)
})

Expand All @@ -139,7 +139,7 @@ func TestUnmarshalText(t *testing.T) {
err := json.Unmarshal([]byte(`{"Name": "Mega Chad", "Job": "Big Boss"}`), &o)
require.NoError(t, err)

expected := Option[S]{ok: true, val: S{"Mega Chad", *Some("Big Boss")}}
expected := Option[S]{ok: true, val: S{"Mega Chad", Some("Big Boss")}}
assert.Equal(t, expected, o)
})

Expand Down
Loading