diff --git a/src/main/java/net/kemitix/mon/result/Err.java b/src/main/java/net/kemitix/mon/result/Err.java index 0e54336..c1fe968 100644 --- a/src/main/java/net/kemitix/mon/result/Err.java +++ b/src/main/java/net/kemitix/mon/result/Err.java @@ -80,6 +80,16 @@ class Err implements Result { return this; } + @Override + public Result recover(final Function> f) { + return f.apply(error); + } + + @Override + public void onError(final Consumer errorConsumer) { + errorConsumer.accept(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 4dd6d65..275ab7b 100644 --- a/src/main/java/net/kemitix/mon/result/Result.java +++ b/src/main/java/net/kemitix/mon/result/Result.java @@ -94,7 +94,7 @@ public interface Result extends Functor> { * Creates a Result from the Maybe, where the Result will be an error if the Maybe is Nothing. * * @param result the Result the might contain the value of the Result - * @param the type of the Maybe and the Result + * @param the type of the Maybe and the Result * @return a Result containing the value of the Maybe when it is a Just, or the error when it is Nothing */ @SuppressWarnings("illegalcatch") @@ -108,8 +108,9 @@ public interface Result extends Functor> { /** * Swaps the inner Result of a Maybe, so that a Result is on the outside. + * * @param maybeResult the Maybe the contains a Result - * @param the type of the value that may be in the Result + * @param the type of the value that may be in the Result * @return a Result containing a Maybe, the value in the Maybe was the value in a successful Result within the * original Maybe. If the original Maybe is Nothing, the Result will contain Nothing. If the original Result was an * error, then the Result will also be an error. @@ -177,4 +178,22 @@ public interface Result extends Functor> { * @return this Result */ Result peek(Consumer consumer); + + /** + * Provide a way to attempt to recover from an error state. + * + * @param f the function to recover from the error + * @return a new Result, either a Success, or if recovery is not possible an other Err. + */ + Result recover(Function> f); + + /** + * A handler for error states. + * + *

When this is an error then tne Consumer will be supplier with the error. When this is a success, then nothing + * happens.

+ * + * @param errorConsumer the consumer to handle the error + */ + void onError(Consumer errorConsumer); } diff --git a/src/main/java/net/kemitix/mon/result/Success.java b/src/main/java/net/kemitix/mon/result/Success.java index 7c880fa..d086218 100644 --- a/src/main/java/net/kemitix/mon/result/Success.java +++ b/src/main/java/net/kemitix/mon/result/Success.java @@ -84,6 +84,16 @@ class Success implements Result { return this; } + @Override + public Result recover(final Function> f) { + return this; + } + + @Override + public void onError(final Consumer errorConsumer) { + // do nothing - this is not an error + } + @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 9e5c52c..19e8439 100644 --- a/src/test/java/net/kemitix/mon/ResultTest.java +++ b/src/test/java/net/kemitix/mon/ResultTest.java @@ -452,6 +452,54 @@ public class ResultTest implements WithAssertions { assertThat(peeked).isSameAs(result); } + @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() { + //given + final RuntimeException exception = new RuntimeException(); + final Result error = Result.error(exception); + final AtomicReference capture = new AtomicReference<>(); + //when + error.onError(capture::set); + //then + assertThat(capture).hasValue(exception); + } + + @Test + public void success_whenRecover_thenNoChange() { + //given + final Result ok = Result.ok(1); + //when + final Result recovered = ok.recover(e -> Result.ok(2)); + //then + recovered.peek(v -> assertThat(v).isEqualTo(1)); + } + + @Test + public void error_whenRecover_thenSuccess() { + //given + final Result error = Result.error(new RuntimeException()); + //when + final Result recovered = error.recover(e -> Result.ok(2)); + //then + recovered.peek(v -> assertThat(v).isEqualTo(2)); + } + + @Test + public void error_whenRecoverFails_thenUpdatedError() { + //given + final Result error = Result.error(new RuntimeException("original")); + //when + final Result recovered = error.recover(e -> Result.error(new RuntimeException("updated"))); + //then + recovered.onError(e -> assertThat(e).hasMessage("updated")); + } + @RequiredArgsConstructor private static class UseCase {