Merge pull request #52 from kemitix/result-or-throw-wraps

[result] orElseThrow() wraps the error exception in an unchecked MonResultException
This commit is contained in:
Paul Campbell 2018-10-04 07:47:42 +01:00 committed by GitHub
commit ef56892af0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 216 additions and 3 deletions

View file

@ -1,6 +1,11 @@
CHANGELOG CHANGELOG
========= =========
1.1.0
-----
* [result] add orElseThrow(Class) and orElseThrowUnchecked()
1.0.0 1.0.0
----- -----

View file

@ -806,6 +806,7 @@
.thenWith(v -> () -> {throw new IOException();}); .thenWith(v -> () -> {throw new IOException();});
#+END_SRC #+END_SRC
**** =Result<Maybe<T>> maybe(Predicate<T> predicate)= **** =Result<Maybe<T>> maybe(Predicate<T> predicate)=
Wraps the value within the Result in a Maybe, either a Just if the Wraps the value within the Result in a Maybe, either a Just if the
@ -820,7 +821,7 @@
**** =T orElseThrow()= **** =T orElseThrow()=
Extracts the successful value from the result, or throws the error Extracts the successful value from the result, or throws the error
Throwable. =Throwable=.
#+BEGIN_SRC java #+BEGIN_SRC java
final Integer result = Result.of(() -> getValue()) final Integer result = Result.of(() -> getValue())
@ -828,6 +829,29 @@
#+END_SRC #+END_SRC
**** =<E extends Exception> T orElseThrow(Class<E> 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<Throwable> errorConsumer)= **** =void onError(Consumer<Throwable> errorConsumer)=
A handler for error states. A handler for error states.
@ -972,6 +996,7 @@
final Lazy<String> stringLazy = uuidLazy.map(v -> v.toString()); final Lazy<String> stringLazy = uuidLazy.map(v -> v.toString());
#+END_SRC #+END_SRC
** Either ** Either
Allows handling a value that can be one of two types, a left value/type or a Allows handling a value that can be one of two types, a left value/type or a

View file

@ -77,6 +77,20 @@ class Err<T> implements Result<T> {
throw error; throw error;
} }
@Override
@SuppressWarnings("unchecked")
public <E extends Exception> T orElseThrow(final Class<E> type) throws E {
if (type.isInstance(error)) {
throw (E) error;
}
throw UnexpectedErrorResultException.with(error);
}
@Override
public T orElseThrowUnchecked() {
throw ErrorResultException.with(error);
}
@Override @Override
public Result<T> peek(final Consumer<T> consumer) { public Result<T> peek(final Consumer<T> consumer) {
return this; return this;

View file

@ -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}.
*
* <p>Used by {@link Result#orElseThrowUnchecked()} when the {@link Result} is an error.</p>
*
* @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);
}
}

View file

@ -99,7 +99,7 @@ public interface Result<T> extends Functor<T, Result<?>> {
@SuppressWarnings("illegalcatch") @SuppressWarnings("illegalcatch")
public static <T> Maybe<T> toMaybe(final Result<T> result) { public static <T> Maybe<T> toMaybe(final Result<T> result) {
try { try {
return Maybe.just(result.orElseThrow()); return Maybe.just(result.orElseThrow(Exception.class));
} catch (final Throwable throwable) { } catch (final Throwable throwable) {
return Maybe.nothing(); return Maybe.nothing();
} }
@ -109,11 +109,29 @@ public interface Result<T> extends Functor<T, Result<?>> {
* Extracts the successful value from the result, or throws the error Throwable. * Extracts the successful value from the result, or throws the error Throwable.
* *
* @return the value if a success * @return the value if a success
* @throws Throwable the result is an error * @throws Throwable if the result is an error
*/ */
@SuppressWarnings("illegalthrows") @SuppressWarnings("illegalthrows")
public abstract T orElseThrow() throws Throwable; 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 <E> the type of the checked exception to throw
*
* @return the value if a success
* @throws E if the result is an error
*/
public abstract <E extends Exception> T orElseThrow(Class<E> 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. * Swaps the inner Result of a Maybe, so that a Result is on the outside.
* *

View file

@ -75,6 +75,16 @@ class Success<T> implements Result<T> {
return value; return value;
} }
@Override
public <E extends Exception> T orElseThrow(final Class<E> type) throws E {
return value;
}
@Override
public T orElseThrowUnchecked() {
return value;
}
@Override @Override
public Result<T> peek(final Consumer<T> consumer) { public Result<T> peek(final Consumer<T> consumer) {
consumer.accept(value); consumer.accept(value);

View file

@ -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}.
*
* <p>Used by {@link Result#orElseThrow(Class)} when the {@link Result} is an error of a different type to the
* parameter.</p>
*
* @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);
}
}

View file

@ -2,6 +2,8 @@ package net.kemitix.mon;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import net.kemitix.mon.maybe.Maybe; 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 net.kemitix.mon.result.Result;
import org.assertj.core.api.WithAssertions; import org.assertj.core.api.WithAssertions;
import org.junit.Test; import org.junit.Test;
@ -295,6 +297,52 @@ public class ResultTest implements WithAssertions {
assertThatThrownBy(() -> error.orElseThrow()).isSameAs(exception); assertThatThrownBy(() -> error.orElseThrow()).isSameAs(exception);
} }
@Test public void okay_whenOrElseThrowT_isValue() throws Exception {
//given
final Result<Integer> 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<Object> 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<Object> error = Result.error(exception);
//then
assertThatThrownBy(() -> error.orElseThrow(RuntimeException.class))
.isInstanceOf(UnexpectedErrorResultException.class)
.hasCause(exception);
}
@Test public void okay_whenOrElseThrowUnchecked_isValue() {
//given
final Result<Integer> 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<Object> error = Result.error(exception);
//then
assertThatThrownBy(() -> error.orElseThrowUnchecked())
.isInstanceOf(ErrorResultException.class)
.hasCause(exception);
}
@Test @Test
public void justOkay_whenInvert_thenOkayJust() { public void justOkay_whenInvert_thenOkayJust() {
//given //given