From c5247d7c14e5e139f2595ff9165eb46e9669702a Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Sat, 7 Jul 2018 18:21:35 +0100 Subject: [PATCH 1/3] Add `Maybe.ifNothing(Runnable)` --- CHANGELOG | 5 ++++ src/main/java/net/kemitix/mon/maybe/Just.java | 5 ++++ .../java/net/kemitix/mon/maybe/Maybe.java | 7 ++++++ .../java/net/kemitix/mon/maybe/Nothing.java | 5 ++++ src/test/java/net/kemitix/mon/MaybeTest.java | 23 +++++++++++++++++++ 5 files changed, 45 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index e1f6ff8..c4444f2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,11 @@ CHANGELOG ========= +0.9.0 +----- + +* Add `Maybe.ifNothing(Runnable)` + 0.8.0 ----- diff --git a/src/main/java/net/kemitix/mon/maybe/Just.java b/src/main/java/net/kemitix/mon/maybe/Just.java index 77a258f..85aea43 100644 --- a/src/main/java/net/kemitix/mon/maybe/Just.java +++ b/src/main/java/net/kemitix/mon/maybe/Just.java @@ -93,6 +93,11 @@ final class Just implements Maybe { return this; } + @Override + public void ifNothing(final Runnable runnable) { + // ignore - not nothing + } + @Override public void orElseThrow(final Supplier e) { // do not throw diff --git a/src/main/java/net/kemitix/mon/maybe/Maybe.java b/src/main/java/net/kemitix/mon/maybe/Maybe.java index 02b1ffc..be04b67 100644 --- a/src/main/java/net/kemitix/mon/maybe/Maybe.java +++ b/src/main/java/net/kemitix/mon/maybe/Maybe.java @@ -142,4 +142,11 @@ public interface Maybe extends Functor> { * @return this Maybe */ Maybe peek(Consumer consumer); + + /** + * Run the runnable if the Maybe is a Nothing, otherwise do nothing. + * + * @param runnable the runnable to call if this is a Nothing + */ + void ifNothing(Runnable runnable); } diff --git a/src/main/java/net/kemitix/mon/maybe/Nothing.java b/src/main/java/net/kemitix/mon/maybe/Nothing.java index 05f033d..d1c7193 100644 --- a/src/main/java/net/kemitix/mon/maybe/Nothing.java +++ b/src/main/java/net/kemitix/mon/maybe/Nothing.java @@ -74,6 +74,11 @@ final class Nothing implements Maybe { return this; } + @Override + public void ifNothing(final Runnable runnable) { + runnable.run(); + } + @Override public void orElseThrow(final Supplier e) throws Exception { throw e.get(); diff --git a/src/test/java/net/kemitix/mon/MaybeTest.java b/src/test/java/net/kemitix/mon/MaybeTest.java index 575826a..2b82337 100644 --- a/src/test/java/net/kemitix/mon/MaybeTest.java +++ b/src/test/java/net/kemitix/mon/MaybeTest.java @@ -6,6 +6,7 @@ import org.junit.Test; import java.util.Objects; import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; import java.util.stream.Stream; @@ -171,4 +172,26 @@ public class MaybeTest implements WithAssertions { assertThat(result.toOptional()).isEmpty(); } + @Test + public void just_ifNothing_isIgnored() { + //given + final Maybe just = Maybe.just(1); + final AtomicBoolean capture = new AtomicBoolean(false); + //when + just.ifNothing(() -> capture.set(true)); + //then + assertThat(capture).isFalse(); + } + + @Test + public void nothing_ifNothing_isCalled() { + //given + final Maybe nothing = Maybe.nothing(); + final AtomicBoolean capture = new AtomicBoolean(false); + //when + nothing.ifNothing(() -> capture.set(true)); + //then + assertThat(capture).isTrue(); + } + } From 28564a2129286aee88111659760ccecd4af76e07 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Sat, 7 Jul 2018 23:02:02 +0100 Subject: [PATCH 2/3] Add Result.recover(Function) and Result.onError(Consumer) --- src/main/java/net/kemitix/mon/result/Err.java | 10 ++++ .../java/net/kemitix/mon/result/Result.java | 23 ++++++++- .../java/net/kemitix/mon/result/Success.java | 10 ++++ src/test/java/net/kemitix/mon/ResultTest.java | 48 +++++++++++++++++++ 4 files changed, 89 insertions(+), 2 deletions(-) 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 { From 5a5fe003d0a9a82feab28c2340494b3ed50e98d2 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Sat, 7 Jul 2018 23:12:28 +0100 Subject: [PATCH 3/3] Update CHANGELOG --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index c4444f2..6948f3b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,6 +5,8 @@ CHANGELOG ----- * Add `Maybe.ifNothing(Runnable)` +* Add `Result.recover(Function)` +* Add `Result.onError(Consumer)` 0.8.0 -----