diff --git a/CHANGELOG b/CHANGELOG index 6948f3b..a8d6a08 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,11 @@ CHANGELOG ========= +0.10.0 +------ + +* Add `andThen(Function)` + 0.9.0 ----- diff --git a/src/main/java/net/kemitix/mon/result/Err.java b/src/main/java/net/kemitix/mon/result/Err.java index c1fe968..ce7ae6d 100644 --- a/src/main/java/net/kemitix/mon/result/Err.java +++ b/src/main/java/net/kemitix/mon/result/Err.java @@ -25,6 +25,7 @@ import lombok.RequiredArgsConstructor; import net.kemitix.mon.maybe.Maybe; import java.util.Objects; +import java.util.concurrent.Callable; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -90,6 +91,11 @@ class Err implements Result { errorConsumer.accept(error); } + @Override + public Result andThen(final Function> f) { + return Result.error(error); + } + @Override public boolean equals(final Object other) { return other instanceof Err && Objects.equals(error, ((Err) other).error); diff --git a/src/main/java/net/kemitix/mon/result/Result.java b/src/main/java/net/kemitix/mon/result/Result.java index 275ab7b..fb15a36 100644 --- a/src/main/java/net/kemitix/mon/result/Result.java +++ b/src/main/java/net/kemitix/mon/result/Result.java @@ -196,4 +196,25 @@ public interface Result extends Functor> { * @param errorConsumer the consumer to handle the error */ void onError(Consumer errorConsumer); + + /** + * Maps a Success Result to another Result using a Callable that is able to throw a checked exception. + * + *

Combination of {@link #flatMap(Function)} and {@link #of(Callable)}.

+ * + *

Syntax is:

+ *

+     *     Integer doSomething() {...}
+     *     String doSomethingElse(final Integer value) {...}
+     *     Result<String> r = Result.of(() -> doSomething())
+     *                              .andThen(value -> () -> doSomethingElse(value));
+     * 
+ * + *

When the Result is an Err, then the original error is carried over and the Callable is never called.

+ * + * @param f the function to map the Success value to the Callable + * @param the type of the final Result + * @return a new Result + */ + Result andThen(Function> f); } diff --git a/src/main/java/net/kemitix/mon/result/Success.java b/src/main/java/net/kemitix/mon/result/Success.java index d086218..ab605c3 100644 --- a/src/main/java/net/kemitix/mon/result/Success.java +++ b/src/main/java/net/kemitix/mon/result/Success.java @@ -25,6 +25,7 @@ import lombok.RequiredArgsConstructor; import net.kemitix.mon.maybe.Maybe; import java.util.Objects; +import java.util.concurrent.Callable; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -94,6 +95,11 @@ class Success implements Result { // do nothing - this is not an error } + @Override + public Result andThen(final Function> f) { + return Result.of(f.apply(value)); + } + @Override public boolean equals(final Object other) { return other instanceof Success && Objects.equals(value, ((Success) other).value); diff --git a/src/test/java/net/kemitix/mon/ResultTest.java b/src/test/java/net/kemitix/mon/ResultTest.java index 19e8439..98e5a4e 100644 --- a/src/test/java/net/kemitix/mon/ResultTest.java +++ b/src/test/java/net/kemitix/mon/ResultTest.java @@ -452,14 +452,16 @@ public class ResultTest implements WithAssertions { assertThat(peeked).isSameAs(result); } - @Test public void success_whenOnError_thenIgnore() { + @Test + public void success_whenOnError_thenIgnore() { //given final Result ok = Result.ok(1); //when ok.onError(e -> fail("not an error")); } - @Test public void error_whenOnError_thenConsume() { + @Test + public void error_whenOnError_thenConsume() { //given final RuntimeException exception = new RuntimeException(); final Result error = Result.error(exception); @@ -500,6 +502,57 @@ public class ResultTest implements WithAssertions { recovered.onError(e -> assertThat(e).hasMessage("updated")); } + @Test + public void success_andThenSuccess_thenSuccess() { + //given + final Result ok = Result.ok(1); + //when + final Result result = ok.andThen(v -> () -> "success"); + //then + assertThat(result.isOkay()).isTrue(); + result.peek(v -> assertThat(v).isEqualTo("success")); + } + + @Test + public void success_andThenError_thenError() { + //given + final Result ok = Result.ok(1); + final RuntimeException exception = new RuntimeException(); + //when + final Result result = ok.andThen(v -> () -> { + throw exception; + }); + //then + assertThat(result.isError()).isTrue(); + result.onError(e -> assertThat(e).isSameAs(exception)); + } + + @Test + public void error_andThenSuccess_thenError() { + //given + final RuntimeException exception = new RuntimeException(); + final Result error = Result.error(exception); + //when + final Result result = error.andThen(v -> () -> "success"); + //then + assertThat(result.isError()).isTrue(); + result.onError(e -> assertThat(e).isSameAs(exception)); + } + + @Test + public void error_andThenError_thenError() { + //given + final RuntimeException exception1 = new RuntimeException(); + final Result error = Result.error(exception1); + //when + final Result result = error.andThen(v -> () -> { + throw new RuntimeException(); + }); + //then + assertThat(result.isError()).isTrue(); + result.onError(e -> assertThat(e).isSameAs(exception1)); + } + @RequiredArgsConstructor private static class UseCase {