diff --git a/CHANGELOG b/CHANGELOG index f0f991d..aaf77f9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,11 @@ CHANGELOG ========= +1.1.0 +----- + +* [result] add orElseThrow(Class) and orElseThrowUnchecked() + 1.0.0 ----- diff --git a/README.org b/README.org index 1550087..266002d 100644 --- a/README.org +++ b/README.org @@ -806,6 +806,7 @@ .thenWith(v -> () -> {throw new IOException();}); #+END_SRC + **** =Result> maybe(Predicate predicate)= Wraps the value within the Result in a Maybe, either a Just if the @@ -820,7 +821,7 @@ **** =T orElseThrow()= Extracts the successful value from the result, or throws the error - Throwable. + =Throwable=. #+BEGIN_SRC java final Integer result = Result.of(() -> getValue()) @@ -828,6 +829,29 @@ #+END_SRC +**** = T orElseThrow(Class type) throws E= + + Extracts the successful value from the result, or throws the error when it + is of the given type. Any other errors will be thrown inside an + =UnexpectedErrorResultException=. + + #+BEGIN_SRC java + final Integer result = Result.of(() -> getValue()) + .orElseThrow(IOException.class); + #+END_SRC + + +**** =T orElseThrowUnchecked()= + + Extracts the successful value from the result, or throws the error within + an =ErrorResultException=. + + #+BEGIN_SRC java + final Integer result = Result.of(() -> getValue()) + .orElseThrowUnchecked(); + #+END_SRC + + **** =void onError(Consumer errorConsumer)= A handler for error states. @@ -972,6 +996,7 @@ final Lazy stringLazy = uuidLazy.map(v -> v.toString()); #+END_SRC + ** Either Allows handling a value that can be one of two types, a left value/type or a diff --git a/src/main/java/net/kemitix/mon/result/Err.java b/src/main/java/net/kemitix/mon/result/Err.java index e2199ad..01bf9b5 100644 --- a/src/main/java/net/kemitix/mon/result/Err.java +++ b/src/main/java/net/kemitix/mon/result/Err.java @@ -77,6 +77,20 @@ class Err implements Result { throw error; } + @Override + @SuppressWarnings("unchecked") + public T orElseThrow(final Class type) throws E { + if (type.isInstance(error)) { + throw (E) error; + } + throw UnexpectedErrorResultException.with(error); + } + + @Override + public T orElseThrowUnchecked() { + throw ErrorResultException.with(error); + } + @Override public Result peek(final Consumer consumer) { return this; diff --git a/src/main/java/net/kemitix/mon/result/ErrorResultException.java b/src/main/java/net/kemitix/mon/result/ErrorResultException.java new file mode 100644 index 0000000..47e3a90 --- /dev/null +++ b/src/main/java/net/kemitix/mon/result/ErrorResultException.java @@ -0,0 +1,46 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 Paul Campbell + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies + * or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE + * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package net.kemitix.mon.result; + +/** + * An unchecked wrapper fot exceptions thrown within a {@link Result}. + * + *

Used by {@link Result#orElseThrowUnchecked()} when the {@link Result} is an error.

+ * + * @author Paul Campbell (pcampbell@kemitix.net) + */ +public final class ErrorResultException extends RuntimeException { + + private ErrorResultException(final Throwable cause) { + super(cause); + } + + /** + * Creates a new object. + * + * @param cause the cause + * @return a {@link ErrorResultException} containing the cause + */ + static ErrorResultException with(final Throwable cause) { + return new ErrorResultException(cause); + } +} diff --git a/src/main/java/net/kemitix/mon/result/Result.java b/src/main/java/net/kemitix/mon/result/Result.java index 86d4819..22a2d81 100644 --- a/src/main/java/net/kemitix/mon/result/Result.java +++ b/src/main/java/net/kemitix/mon/result/Result.java @@ -99,7 +99,7 @@ public interface Result extends Functor> { @SuppressWarnings("illegalcatch") public static Maybe toMaybe(final Result result) { try { - return Maybe.just(result.orElseThrow()); + return Maybe.just(result.orElseThrow(Exception.class)); } catch (final Throwable throwable) { return Maybe.nothing(); } @@ -109,11 +109,29 @@ public interface Result extends Functor> { * Extracts the successful value from the result, or throws the error Throwable. * * @return the value if a success - * @throws Throwable the result is an error + * @throws Throwable if the result is an error */ @SuppressWarnings("illegalthrows") public abstract T orElseThrow() throws Throwable; + /** + * Extracts the successful value from the result, or throws the error Throwable. + * + * @param type the type of checked exception that may be thrown + * @param the type of the checked exception to throw + * + * @return the value if a success + * @throws E if the result is an error + */ + public abstract T orElseThrow(Class type) throws E; + + /** + * Extracts the successful value from the result, or throws the error in a {@link UnexpectedErrorResultException}. + * + * @return the value if a success + */ + public abstract T orElseThrowUnchecked(); + /** * Swaps the inner Result of a Maybe, so that a Result is on the outside. * diff --git a/src/main/java/net/kemitix/mon/result/Success.java b/src/main/java/net/kemitix/mon/result/Success.java index e79e54e..ce49401 100644 --- a/src/main/java/net/kemitix/mon/result/Success.java +++ b/src/main/java/net/kemitix/mon/result/Success.java @@ -75,6 +75,16 @@ class Success implements Result { return value; } + @Override + public T orElseThrow(final Class type) throws E { + return value; + } + + @Override + public T orElseThrowUnchecked() { + return value; + } + @Override public Result peek(final Consumer consumer) { consumer.accept(value); diff --git a/src/main/java/net/kemitix/mon/result/UnexpectedErrorResultException.java b/src/main/java/net/kemitix/mon/result/UnexpectedErrorResultException.java new file mode 100644 index 0000000..ef67f8c --- /dev/null +++ b/src/main/java/net/kemitix/mon/result/UnexpectedErrorResultException.java @@ -0,0 +1,47 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 Paul Campbell + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies + * or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE + * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package net.kemitix.mon.result; + +/** + * An Unchecked wrapper for unexpected exceptions thrown within a {@link Result}. + * + *

Used by {@link Result#orElseThrow(Class)} when the {@link Result} is an error of a different type to the + * parameter.

+ * + * @author Paul Campbell (pcampbell@kemitix.net) + */ +public final class UnexpectedErrorResultException extends RuntimeException { + + private UnexpectedErrorResultException(final Throwable cause) { + super(cause); + } + + /** + * Creates a new object. + * + * @param cause the cause + * @return a {@link UnexpectedErrorResultException} containing the cause + */ + static UnexpectedErrorResultException with(final Throwable cause) { + return new UnexpectedErrorResultException(cause); + } +} diff --git a/src/test/java/net/kemitix/mon/ResultTest.java b/src/test/java/net/kemitix/mon/ResultTest.java index 3805a24..a8cc577 100644 --- a/src/test/java/net/kemitix/mon/ResultTest.java +++ b/src/test/java/net/kemitix/mon/ResultTest.java @@ -2,6 +2,8 @@ package net.kemitix.mon; import lombok.RequiredArgsConstructor; import net.kemitix.mon.maybe.Maybe; +import net.kemitix.mon.result.ErrorResultException; +import net.kemitix.mon.result.UnexpectedErrorResultException; import net.kemitix.mon.result.Result; import org.assertj.core.api.WithAssertions; import org.junit.Test; @@ -295,6 +297,52 @@ public class ResultTest implements WithAssertions { assertThatThrownBy(() -> error.orElseThrow()).isSameAs(exception); } + @Test public void okay_whenOrElseThrowT_isValue() throws Exception { + //given + final Result ok = Result.ok(1); + //when + final Integer value = ok.orElseThrow(Exception.class); + //then + assumeThat(value).isEqualTo(1); + } + + @Test public void errorT_whenOrElseThrowT_throwsT() { + //given + final RuntimeException exception = new RuntimeException(); + final Result error = Result.error(exception); + //then + assertThatThrownBy(() -> error.orElseThrow(RuntimeException.class)).isSameAs(exception); + } + + @Test public void errorR_whenOrElseThrowT_throwsWrappedR() { + //given + final IOException exception = new IOException(); + final Result error = Result.error(exception); + //then + assertThatThrownBy(() -> error.orElseThrow(RuntimeException.class)) + .isInstanceOf(UnexpectedErrorResultException.class) + .hasCause(exception); + } + + @Test public void okay_whenOrElseThrowUnchecked_isValue() { + //given + final Result ok = Result.ok(1); + //when + final Integer value = ok.orElseThrowUnchecked(); + //then + assumeThat(value).isEqualTo(1); + } + + @Test public void error_whenOrElseThrowUnchecked_throwsWrapped() { + //given + final IOException exception = new IOException(); + final Result error = Result.error(exception); + //then + assertThatThrownBy(() -> error.orElseThrowUnchecked()) + .isInstanceOf(ErrorResultException.class) + .hasCause(exception); + } + @Test public void justOkay_whenInvert_thenOkayJust() { //given