diff --git a/option.go b/option.go index 5044df9..60b347d 100644 --- a/option.go +++ b/option.go @@ -153,6 +153,22 @@ func (o Option[T]) Map(mapper func(value T) (T, bool)) Option[T] { return None[T]() } +func (o Option[T]) Maps(mapperList ...func(value T) (T, bool)) Option[T] { + if !o.isPresent { + return None[T]() + } + tmpValue := o.value + for _, mapper := range mapperList { + var ok bool + tmpValue, ok = mapper(tmpValue) + if !ok { + return None[T]() + } + } + + return Some(tmpValue) +} + // MapNone executes the mapper function if value is absent or returns Option. // Play: https://go.dev/play/p/_KaHWZ6Q17b func (o Option[T]) MapNone(mapper func() (T, bool)) Option[T] { diff --git a/option_example_test.go b/option_example_test.go index 956243e..280b12c 100644 --- a/option_example_test.go +++ b/option_example_test.go @@ -258,6 +258,34 @@ func ExampleOption_Map_none() { // Output: false 0 } +func ExampleOption_Maps_some() { + some := Some(42) + result := some.Maps( + func(i int) (int, bool) { + return 1234, true + }, + func(value int) (int, bool) { + return value + 1, true + }, + ) + + fmt.Println(result.IsPresent(), result.OrEmpty()) + // Output: true 1235 +} +func ExampleOption_Maps_none() { + none := None[int]() + result := none.Maps( + func(i int) (int, bool) { + return 1234, true + }, + func(value int) (int, bool) { + return value + 1, true + }, + ) + + fmt.Println(result.IsPresent(), result.OrEmpty()) + // Output: false 0 +} func ExampleOption_MapNone_some() { some := Some(42) result := some.MapNone(func() (int, bool) { diff --git a/option_test.go b/option_test.go index 2ecf43f..2836cd0 100644 --- a/option_test.go +++ b/option_test.go @@ -173,6 +173,43 @@ func TestOptionMap(t *testing.T) { is.Equal(Option[int]{value: 0, isPresent: false}, opt2) } +func TestOptionMaps(t *testing.T) { + is := assert.New(t) + + mul2 := func(i int) (int, bool) { + return i * 2, true + } + inc1 := func(i int) (int, bool) { + return i + 1, true + } + returnFalse := func(i int) (int, bool) { + return 0, false + } + t.Run("maps 2 func ok ", func(t *testing.T) { + v := Some(21).Maps(mul2, inc1) + is.Equal(Option[int]{value: 43, isPresent: true}, v) + }) + t.Run("maps first ok func, second failed func", func(t *testing.T) { + v := Some(21).Maps(mul2, returnFalse) + is.False(v.IsPresent()) + }) + + t.Run("maps 2 failed func", func(t *testing.T) { + v := Some(21).Maps(returnFalse, func(i int) (int, bool) { + is.Fail("should not be called") + return 42, true + }) + is.False(v.IsPresent()) + }) + t.Run("maps none", func(t *testing.T) { + v := None[int]().Maps(mul2, func(i int) (int, bool) { + is.Fail("should not be called") + return 42, true + }) + is.False(v.IsPresent()) + }) +} + func TestOptionMapNone(t *testing.T) { is := assert.New(t) diff --git a/result.go b/result.go index a9f58da..4925624 100644 --- a/result.go +++ b/result.go @@ -146,6 +146,21 @@ func (r Result[T]) Map(mapper func(value T) (T, error)) Result[T] { return Err[T](r.err) } +func (r Result[T]) Maps(mapperList ...func(value T) (T, error)) Result[T] { + if r.isErr { + return Err[T](r.err) + } + + tmpValue := r.value + for _, mapper := range mapperList { + var err error + tmpValue, err = mapper(tmpValue) + if err != nil { + return Err[T](err) + } + } + return Ok[T](tmpValue) +} // MapErr executes the mapper function if Result is invalid. It returns a new Result. // Play: https://go.dev/play/p/WraZixg9GGf diff --git a/result_example_test.go b/result_example_test.go index bc8e9dc..b720540 100644 --- a/result_example_test.go +++ b/result_example_test.go @@ -299,6 +299,36 @@ func ExampleResult_Map_err() { // Output: true 0 error } +func ExampleResult_Maps_ok() { + ok := Ok(42) + result := ok.Maps( + func(i int) (int, error) { + return i * 2, nil + }, + func(i int) (int, error) { + return i + 1, nil + }, + ) + + fmt.Println(result.IsError(), result.OrEmpty(), result.Error()) + // Output: false 85 +} + +func ExampleResult_Maps_err() { + ko := Err[int](err) + result := ko.Maps( + func(i int) (int, error) { + return i * 2, nil + }, + func(i int) (int, error) { + return i + 1, nil + }, + ) + + fmt.Println(result.IsError(), result.OrEmpty(), result.Error()) + // Output: true 0 error +} + func ExampleResult_MapErr_ok() { ok := Ok(42) result := ok.MapErr( diff --git a/result_test.go b/result_test.go index 0130d64..7d91afc 100644 --- a/result_test.go +++ b/result_test.go @@ -174,6 +174,42 @@ func TestResultMap(t *testing.T) { is.Equal(Result[int]{value: 0, isErr: true, err: assert.AnError}, opt2) } +func TestResultMaps(t *testing.T) { + is := assert.New(t) + mul2 := func(i int) (int, error) { + return i * 2, nil + } + inc1 := func(i int) (int, error) { + return i + 1, nil + } + returnErr := func(i int) (int, error) { + return 0, assert.AnError + } + t.Run("maps 2 func ok ", func(t *testing.T) { + v := Ok(21).Maps(mul2, inc1) + is.Equal(Result[int]{value: 43, isErr: false, err: nil}, v) + }) + t.Run("maps first ok func, second failed func", func(t *testing.T) { + v := Ok(21).Maps(mul2, returnErr) + is.Equal(Result[int]{value: 0, isErr: true, err: assert.AnError}, v) + }) + + t.Run("maps 2 failed func", func(t *testing.T) { + v := Ok(21).Maps(returnErr, func(i int) (int, error) { + is.Fail("should not be called") + return 42, nil + }) + is.Equal(Result[int]{value: 0, isErr: true, err: assert.AnError}, v) + }) + t.Run("errors maps ", func(t *testing.T) { + v := Err[int](assert.AnError).Maps(mul2, func(i int) (int, error) { + is.Fail("should not be called") + return 42, nil + }) + is.Equal(Result[int]{value: 0, isErr: true, err: assert.AnError}, v) + }) +} + func TestResultMapErr(t *testing.T) { is := assert.New(t)