From 5e13cbb9a2856452bb46db26c7b1d08ce302dc25 Mon Sep 17 00:00:00 2001 From: Petr Portnov Date: Sun, 7 Nov 2021 00:59:55 +0300 Subject: [PATCH 1/6] fix(java-commons): catch `Throwable` in `Result#tryGet(..)` --- .../java/ru/progrm_jarvis/javacommons/object/Result.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java-commons/src/main/java/ru/progrm_jarvis/javacommons/object/Result.java b/java-commons/src/main/java/ru/progrm_jarvis/javacommons/object/Result.java index e7e26f0f..962cafb8 100644 --- a/java-commons/src/main/java/ru/progrm_jarvis/javacommons/object/Result.java +++ b/java-commons/src/main/java/ru/progrm_jarvis/javacommons/object/Result.java @@ -164,13 +164,13 @@ public interface Result extends Supplier { * @return {@link #success(Object) successful result} if the supplier provides the value unexceptionally * or an {@link #error(Object) error result} containing the thrown {@link Throwable throwable} otherwise */ - static Result tryGet( + static Result tryGet( final @NonNull ThrowingSupplier supplier ) { final T value; try { - value = supplier.get(); - } catch (final Exception x) { + value = supplier.getChecked(); + } catch (final Throwable x) { return error(x); } From b95e6b5c38fa9f6a84440ff00dd3f7f705afe86a Mon Sep 17 00:00:00 2001 From: Petr Portnov Date: Sun, 7 Nov 2021 01:00:42 +0300 Subject: [PATCH 2/6] style(java-commons): add missing space after `implements` section of `ThrowingRunnable` --- .../javacommons/util/function/ThrowingRunnable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java-commons/src/main/java/ru/progrm_jarvis/javacommons/util/function/ThrowingRunnable.java b/java-commons/src/main/java/ru/progrm_jarvis/javacommons/util/function/ThrowingRunnable.java index de43c06d..98c246b2 100644 --- a/java-commons/src/main/java/ru/progrm_jarvis/javacommons/util/function/ThrowingRunnable.java +++ b/java-commons/src/main/java/ru/progrm_jarvis/javacommons/util/function/ThrowingRunnable.java @@ -13,7 +13,7 @@ * @param the type of the exception thrown by the function */ @FunctionalInterface -public interface ThrowingRunnable extends Runnable{ +public interface ThrowingRunnable extends Runnable { /** * Runs an action. From 3bcade1f981c0f78bafef49dcf74b321e53ca0b9 Mon Sep 17 00:00:00 2001 From: Petr Portnov Date: Sun, 7 Nov 2021 01:30:48 +0300 Subject: [PATCH 3/6] feat(java-commons): add methods for type specific error handling --- .../javacommons/object/Result.java | 108 ++++++++++++- .../javacommons/object/ResultTest.java | 142 ++++++++++++++++++ 2 files changed, 246 insertions(+), 4 deletions(-) create mode 100644 java-commons/src/test/java/ru/progrm_jarvis/javacommons/object/ResultTest.java diff --git a/java-commons/src/main/java/ru/progrm_jarvis/javacommons/object/Result.java b/java-commons/src/main/java/ru/progrm_jarvis/javacommons/object/Result.java index 962cafb8..f79da5c9 100644 --- a/java-commons/src/main/java/ru/progrm_jarvis/javacommons/object/Result.java +++ b/java-commons/src/main/java/ru/progrm_jarvis/javacommons/object/Result.java @@ -8,6 +8,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import ru.progrm_jarvis.javacommons.annotation.Any; +import ru.progrm_jarvis.javacommons.util.SneakyThrower; +import ru.progrm_jarvis.javacommons.util.TypeHints; +import ru.progrm_jarvis.javacommons.util.TypeHints.TypeHint; import ru.progrm_jarvis.javacommons.util.function.ThrowingRunnable; import ru.progrm_jarvis.javacommons.util.function.ThrowingSupplier; @@ -157,15 +160,65 @@ public interface Result extends Supplier { return nullSuccess(); } + /** + * Creates a result by running the specified runnable. + * + * @param runnable function whose failure indicates the {@link #error(Object) error result} + * @param throwableType class instance representing the type of the thrown exception + * @param type of the thrown throwable + * @return {@link #nullSuccess() successful void-result} if the runnable runs unexceptionally + * or an {@link #error(Object) error result} containing the thrown {@link Throwable throwable} + * if {@link Class#isAssignableFrom(Class) it is of} the expected type + * @apiNote if an unexpected exception is thrown then it will be rethrown + */ + @SuppressWarnings("unchecked") + static @NotNull Result<@Nullable Void, @NotNull X> tryRunChecked( + final @NonNull ThrowingRunnable runnable, + final @NonNull Class throwableType + ) { + //noinspection OverlyBroadCatchBlock: cannot catch generic exception type + try { + runnable.runChecked(); + } catch (final Throwable x) { + if (throwableType.isAssignableFrom(x.getClass())) return error((X) x); + + return SneakyThrower.sneakyThrow(x); + } + + return nullSuccess(); + } + + /** + * Creates a result by running the specified runnable. + * + * @param runnable function whose failure indicates the {@link #error(Object) error result} + * @param throwableTypeHint array used for throwable type discovery + * @param type of the thrown throwable + * @return {@link #nullSuccess() successful void-result} if the runnable runs unexceptionally + * or an {@link #error(Object) error result} containing the thrown {@link Throwable throwable} + * if {@link Class#isAssignableFrom(Class) it is of} the expected type + * @apiNote if an unexpected exception is thrown then it will be rethrown + */ + @SafeVarargs + static @NotNull Result<@Nullable Void, @NotNull X> tryRunChecked( + final @NonNull ThrowingRunnable runnable, + @TypeHint final @Nullable X @NonNull ... throwableTypeHint + ) { + return tryRunChecked(runnable, TypeHints.resolve(throwableTypeHint)); + } + /** * Creates a result by getting the value of the specified supplier. * + * @param type of the successful result value * @param supplier provider of the result whose failure indicates the {@link #error(Object) error result} * @return {@link #success(Object) successful result} if the supplier provides the value unexceptionally - * or an {@link #error(Object) error result} containing the thrown {@link Throwable throwable} otherwise + * or an {@link #error(Object) error result} containing the thrown {@link Throwable throwable} + * if {@link Class#isAssignableFrom(Class) it is of} the expected type + * @apiNote if an unexpected exception is thrown then it will be rethrown */ static Result tryGet( - final @NonNull ThrowingSupplier supplier + final @NonNull ThrowingSupplier supplier ) { final T value; try { @@ -177,11 +230,58 @@ public interface Result extends Supplier { return success(value); } + /** + * Creates a result by getting the value of the specified supplier. + * + * @param supplier provider of the result whose failure indicates the {@link #error(Object) error result} + * @param throwableType class instance representing the type of the thrown exception + * @param type of the {@link #success(Object) successful result} provided by the given supplier + * @return {@link #success(Object) successful result} if the supplier provides the value unexceptionally + * or an {@link #error(Object) error result} containing the thrown {@link Throwable throwable} + * if {@link Class#isAssignableFrom(Class) it is of} the expected type + * @apiNote if an unexpected exception is thrown then it will be rethrown + */ + @SuppressWarnings("unchecked") + static Result tryGetChecked( + final @NonNull ThrowingSupplier supplier, + final @NonNull Class throwableType + ) { + final T value; + //noinspection OverlyBroadCatchBlock: cannot catch generic exception type + try { + value = supplier.getChecked(); + } catch (final Throwable x) { + if (throwableType.isAssignableFrom(x.getClass())) return error((X) x); + + return SneakyThrower.sneakyThrow(x); + } + + return success(value); + } + + /** + * Creates a result by getting the value of the specified supplier. + * + * @param supplier provider of the result whose failure indicates the {@link #error(Object) error result} + * @param throwableTypeHint array used for throwable type discovery + * @param type of the thrown throwable + * @param type of the {@link #success(Object) successful result} provided by the given supplier + * @return {@link #success(Object) successful result} if the supplier provides the value unexceptionally + * or an {@link #error(Object) error result} containing the thrown {@link Throwable throwable} otherwise + */ + @SafeVarargs + static Result tryGetChecked( + final @NonNull ThrowingSupplier supplier, + @TypeHint final @Nullable X @NonNull ... throwableTypeHint + ) { + return tryGetChecked(supplier, TypeHints.resolve(throwableTypeHint)); + } + /** * Creates a result by calling the specified callable. * * @param callable provider of the result whose failure indicates the {@link #error(Object) error result} - * @param type of the result provided by the given callable + * @param type of the {@link #success(Object) successful result} provided by the given supplier * @return {@link #success(Object) successful result} if the callable completes unexceptionally * or an {@link #error(Object) error result} containing the thrown {@link Exception exception} otherwise */ @@ -200,7 +300,7 @@ public interface Result extends Supplier { * Creates a result by calling the specified callable. * * @param callable provider of the result whose failure indicates the {@link #error(Object) error result} - * @param type of the result provided by the given callable + * @param type of the {@link #success(Object) successful result} provided by the given supplier * @return {@link #success(Object) successful result} if the callable completes unexceptionally * or an {@link #error(Object) error result} containing the thrown {@link Exception exception} otherwise * @deprecated in favor of {@link #tryCall(Callable)} diff --git a/java-commons/src/test/java/ru/progrm_jarvis/javacommons/object/ResultTest.java b/java-commons/src/test/java/ru/progrm_jarvis/javacommons/object/ResultTest.java new file mode 100644 index 00000000..f66d78d9 --- /dev/null +++ b/java-commons/src/test/java/ru/progrm_jarvis/javacommons/object/ResultTest.java @@ -0,0 +1,142 @@ +package ru.progrm_jarvis.javacommons.object; + +import lombok.NonNull; +import lombok.experimental.StandardException; +import lombok.experimental.UtilityClass; +import lombok.val; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.*; + +class ResultTest { + + @Test + void test_tryRunChecked_withInferredType_success() { + assertTrue( + Result.tryRunChecked(TestMethods::voidMethodNotThrowingDeclaredIOException).isSuccess() + ); + } + + @Test + void test_tryRunChecked_withInferredType_error() { + val thrown = new IOException("Expected exception"); + assertSame(thrown, + Result.tryRunChecked(() -> TestMethods.voidMethodThrowingDeclaredIOException(thrown)).unwrapError() + ); + } + + @Test + void test_tryRunChecked_withInferredType_rethrow() { + val thrown = new RuntimeException("You didn't expect it, did you?"); + assertSame(thrown, + assertThrows(RuntimeException.class, () -> Result.tryRunChecked( + () -> TestMethods.voidMethodNotThrowingDeclaredIOExceptionButRuntimeException(thrown) + )) + ); + } + + @Test + void test_tryRunChecked_withInferredType_typeInference() { + assertTrue( + Result.tryRunChecked(() -> TestMethods.voidMethodThrowingCustomException(new CustomException())) + .peek(aVoid -> fail("Result should be an error result")) + .peekError(customError -> customError.thisIsACustomError()) + .isError() + ); + } + + @Test + void test_tryGetChecked_withInferredType_success() { + val returned = "Hello world"; + assertSame(returned, + Result.tryGetChecked(() -> TestMethods.stringMethodNotThrowingDeclaredIOException(returned)).unwrap() + ); + } + + @Test + void test_tryGetChecked_withInferredType_error() { + val thrown = new IOException("Expected exception"); + assertSame(thrown, + Result.tryGetChecked(() -> TestMethods.stringMethodThrowingDeclaredIOException(thrown)).unwrapError() + ); + } + + @Test + void test_tryGetChecked_withInferredType_rethrow() { + val thrown = new RuntimeException("You didn't expect it, did you?"); + assertSame(thrown, + assertThrows(RuntimeException.class, () -> Result.tryGetChecked( + () -> TestMethods.stringMethodNotThrowingDeclaredIOExceptionButRuntimeException(thrown) + )) + ); + } + + @Test + void test_tryGetChecked_withInferredType_typeInference() { + assertTrue( + Result.tryGetChecked(() -> TestMethods.stringMethodThrowingCustomException(new CustomException())) + .peek(string -> fail("Result should be an error result")) + .peekError(customError -> customError.thisIsACustomError()) + .isError() + ); + } + + @UtilityClass + private class TestMethods { + + @SuppressWarnings("RedundantThrows") + private void voidMethodNotThrowingDeclaredIOException() throws IOException {} + + private void voidMethodThrowingDeclaredIOException( + final @NonNull IOException thrown + ) throws IOException { + throw thrown; + } + + @SuppressWarnings("RedundantThrows") + private void voidMethodNotThrowingDeclaredIOExceptionButRuntimeException( + final @NonNull RuntimeException thrown + ) throws IOException { + throw thrown; + } + + @SuppressWarnings("RedundantThrows") + private String stringMethodNotThrowingDeclaredIOException(final String returned) throws IOException { + return returned; + } + + private String stringMethodThrowingDeclaredIOException( + final @NonNull IOException thrown + ) throws IOException { + throw thrown; + } + + @SuppressWarnings("RedundantThrows") + private String stringMethodNotThrowingDeclaredIOExceptionButRuntimeException( + final @NonNull RuntimeException thrown + ) throws IOException { + throw thrown; + } + + @SuppressWarnings("RedundantThrows") + private void voidMethodThrowingCustomException( + final @NonNull CustomException customException + ) throws CustomException { + throw customException; + } + + @SuppressWarnings("RedundantThrows") + private String stringMethodThrowingCustomException( + final @NonNull CustomException customException + ) throws CustomException { + throw customException; + } + } + + @StandardException + private final class CustomException extends Exception { + public void thisIsACustomError() {} + } +} \ No newline at end of file From 746ee7dee90005a0b93aaaf325bdb33900f0a947 Mon Sep 17 00:00:00 2001 From: Petr Portnov Date: Sun, 7 Nov 2021 02:29:42 +0300 Subject: [PATCH 4/6] refactor(java-commons): use `isInstance(..)` instead of `isAssignableFrom(..)` in `Result` --- .../progrm_jarvis/javacommons/object/Result.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/java-commons/src/main/java/ru/progrm_jarvis/javacommons/object/Result.java b/java-commons/src/main/java/ru/progrm_jarvis/javacommons/object/Result.java index f79da5c9..2c324047 100644 --- a/java-commons/src/main/java/ru/progrm_jarvis/javacommons/object/Result.java +++ b/java-commons/src/main/java/ru/progrm_jarvis/javacommons/object/Result.java @@ -168,7 +168,7 @@ public interface Result extends Supplier { * @param type of the thrown throwable * @return {@link #nullSuccess() successful void-result} if the runnable runs unexceptionally * or an {@link #error(Object) error result} containing the thrown {@link Throwable throwable} - * if {@link Class#isAssignableFrom(Class) it is of} the expected type + * if {@link Class#isInstance(Object) it is of} the expected type * @apiNote if an unexpected exception is thrown then it will be rethrown */ @SuppressWarnings("unchecked") @@ -180,7 +180,7 @@ public interface Result extends Supplier { try { runnable.runChecked(); } catch (final Throwable x) { - if (throwableType.isAssignableFrom(x.getClass())) return error((X) x); + if (throwableType.isInstance(x)) return error((X) x); return SneakyThrower.sneakyThrow(x); } @@ -196,7 +196,7 @@ public interface Result extends Supplier { * @param type of the thrown throwable * @return {@link #nullSuccess() successful void-result} if the runnable runs unexceptionally * or an {@link #error(Object) error result} containing the thrown {@link Throwable throwable} - * if {@link Class#isAssignableFrom(Class) it is of} the expected type + * if {@link Class#isInstance(Object) it is of} the expected type * @apiNote if an unexpected exception is thrown then it will be rethrown */ @SafeVarargs @@ -214,8 +214,6 @@ public interface Result extends Supplier { * @param supplier provider of the result whose failure indicates the {@link #error(Object) error result} * @return {@link #success(Object) successful result} if the supplier provides the value unexceptionally * or an {@link #error(Object) error result} containing the thrown {@link Throwable throwable} - * if {@link Class#isAssignableFrom(Class) it is of} the expected type - * @apiNote if an unexpected exception is thrown then it will be rethrown */ static Result tryGet( final @NonNull ThrowingSupplier supplier @@ -238,7 +236,8 @@ public interface Result extends Supplier { * @param type of the {@link #success(Object) successful result} provided by the given supplier * @return {@link #success(Object) successful result} if the supplier provides the value unexceptionally * or an {@link #error(Object) error result} containing the thrown {@link Throwable throwable} - * if {@link Class#isAssignableFrom(Class) it is of} the expected type + * if {@link Class#isInstance(Object) it is of} the expected type + * if {@link Class#isInstance(Object) it is of} the expected type * @apiNote if an unexpected exception is thrown then it will be rethrown */ @SuppressWarnings("unchecked") @@ -251,7 +250,7 @@ public interface Result extends Supplier { try { value = supplier.getChecked(); } catch (final Throwable x) { - if (throwableType.isAssignableFrom(x.getClass())) return error((X) x); + if (throwableType.isInstance(x)) return error((X) x); return SneakyThrower.sneakyThrow(x); } @@ -268,6 +267,8 @@ public interface Result extends Supplier { * @param type of the {@link #success(Object) successful result} provided by the given supplier * @return {@link #success(Object) successful result} if the supplier provides the value unexceptionally * or an {@link #error(Object) error result} containing the thrown {@link Throwable throwable} otherwise + * if {@link Class#isInstance(Object) it is of} the expected type + * @apiNote if an unexpected exception is thrown then it will be rethrown */ @SafeVarargs static Result tryGetChecked( From bf71caf6bc9a2a870ba8c58f44a496ec76061222 Mon Sep 17 00:00:00 2001 From: Petr Portnov Date: Sun, 7 Nov 2021 02:35:20 +0300 Subject: [PATCH 5/6] feat(java-commons): give higher priority to type-inferred `try`-methods of `Result` --- .../javacommons/object/Result.java | 70 +++++++++---------- .../javacommons/object/ResultTest.java | 36 +++++----- 2 files changed, 53 insertions(+), 53 deletions(-) diff --git a/java-commons/src/main/java/ru/progrm_jarvis/javacommons/object/Result.java b/java-commons/src/main/java/ru/progrm_jarvis/javacommons/object/Result.java index 2c324047..85b67161 100644 --- a/java-commons/src/main/java/ru/progrm_jarvis/javacommons/object/Result.java +++ b/java-commons/src/main/java/ru/progrm_jarvis/javacommons/object/Result.java @@ -141,25 +141,6 @@ public interface Result extends Supplier { return optional.>map(Result::success).orElseGet(() -> error(errorSupplier.get())); } - /** - * Creates a result by running the specified runnable. - * - * @param runnable function whose failure indicates the {@link #error(Object) error result} - * @return {@link #nullSuccess() successful void-result} if the runnable runs unexceptionally - * or an {@link #error(Object) error result} containing the thrown {@link Throwable throwable} otherwise - */ - static @NotNull Result<@Nullable Void, ? extends @NotNull Throwable> tryRun( - final @NonNull ThrowingRunnable runnable - ) { - try { - runnable.runChecked(); - } catch (final Throwable x) { - return error(x); - } - - return nullSuccess(); - } - /** * Creates a result by running the specified runnable. * @@ -172,7 +153,7 @@ public interface Result extends Supplier { * @apiNote if an unexpected exception is thrown then it will be rethrown */ @SuppressWarnings("unchecked") - static @NotNull Result<@Nullable Void, @NotNull X> tryRunChecked( + static @NotNull Result<@Nullable Void, @NotNull X> tryRun( final @NonNull ThrowingRunnable runnable, final @NonNull Class throwableType ) { @@ -200,32 +181,30 @@ public interface Result extends Supplier { * @apiNote if an unexpected exception is thrown then it will be rethrown */ @SafeVarargs - static @NotNull Result<@Nullable Void, @NotNull X> tryRunChecked( + static @NotNull Result<@Nullable Void, @NotNull X> tryRun( final @NonNull ThrowingRunnable runnable, @TypeHint final @Nullable X @NonNull ... throwableTypeHint ) { - return tryRunChecked(runnable, TypeHints.resolve(throwableTypeHint)); + return tryRun(runnable, TypeHints.resolve(throwableTypeHint)); } /** - * Creates a result by getting the value of the specified supplier. + * Creates a result by running the specified runnable. * - * @param type of the successful result value - * @param supplier provider of the result whose failure indicates the {@link #error(Object) error result} - * @return {@link #success(Object) successful result} if the supplier provides the value unexceptionally - * or an {@link #error(Object) error result} containing the thrown {@link Throwable throwable} + * @param runnable function whose failure indicates the {@link #error(Object) error result} + * @return {@link #nullSuccess() successful void-result} if the runnable runs unexceptionally + * or an {@link #error(Object) error result} containing the thrown {@link Throwable throwable} otherwise */ - static Result tryGet( - final @NonNull ThrowingSupplier supplier + static @NotNull Result<@Nullable Void, ? extends @NotNull Throwable> tryRunCatchAny( + final @NonNull ThrowingRunnable runnable ) { - final T value; try { - value = supplier.getChecked(); + runnable.runChecked(); } catch (final Throwable x) { return error(x); } - return success(value); + return nullSuccess(); } /** @@ -241,7 +220,7 @@ public interface Result extends Supplier { * @apiNote if an unexpected exception is thrown then it will be rethrown */ @SuppressWarnings("unchecked") - static Result tryGetChecked( + static Result tryGet( final @NonNull ThrowingSupplier supplier, final @NonNull Class throwableType ) { @@ -271,11 +250,32 @@ public interface Result extends Supplier { * @apiNote if an unexpected exception is thrown then it will be rethrown */ @SafeVarargs - static Result tryGetChecked( + static Result tryGet( final @NonNull ThrowingSupplier supplier, @TypeHint final @Nullable X @NonNull ... throwableTypeHint ) { - return tryGetChecked(supplier, TypeHints.resolve(throwableTypeHint)); + return tryGet(supplier, TypeHints.resolve(throwableTypeHint)); + } + + /** + * Creates a result by getting the value of the specified supplier. + * + * @param type of the successful result value + * @param supplier provider of the result whose failure indicates the {@link #error(Object) error result} + * @return {@link #success(Object) successful result} if the supplier provides the value unexceptionally + * or an {@link #error(Object) error result} containing the thrown {@link Throwable throwable} + */ + static Result tryGetCatchAny( + final @NonNull ThrowingSupplier supplier + ) { + final T value; + try { + value = supplier.getChecked(); + } catch (final Throwable x) { + return error(x); + } + + return success(value); } /** diff --git a/java-commons/src/test/java/ru/progrm_jarvis/javacommons/object/ResultTest.java b/java-commons/src/test/java/ru/progrm_jarvis/javacommons/object/ResultTest.java index f66d78d9..391fde19 100644 --- a/java-commons/src/test/java/ru/progrm_jarvis/javacommons/object/ResultTest.java +++ b/java-commons/src/test/java/ru/progrm_jarvis/javacommons/object/ResultTest.java @@ -13,72 +13,72 @@ class ResultTest { @Test - void test_tryRunChecked_withInferredType_success() { + void test_tryRun_withInferredType_success() { assertTrue( - Result.tryRunChecked(TestMethods::voidMethodNotThrowingDeclaredIOException).isSuccess() + Result.tryRun(TestMethods::voidMethodNotThrowingDeclaredIOException).isSuccess() ); } @Test - void test_tryRunChecked_withInferredType_error() { + void test_tryRun_withInferredType_error() { val thrown = new IOException("Expected exception"); assertSame(thrown, - Result.tryRunChecked(() -> TestMethods.voidMethodThrowingDeclaredIOException(thrown)).unwrapError() + Result.tryRun(() -> TestMethods.voidMethodThrowingDeclaredIOException(thrown)).unwrapError() ); } @Test - void test_tryRunChecked_withInferredType_rethrow() { + void test_tryRun_withInferredType_rethrow() { val thrown = new RuntimeException("You didn't expect it, did you?"); assertSame(thrown, - assertThrows(RuntimeException.class, () -> Result.tryRunChecked( + assertThrows(RuntimeException.class, () -> Result.tryRun( () -> TestMethods.voidMethodNotThrowingDeclaredIOExceptionButRuntimeException(thrown) )) ); } @Test - void test_tryRunChecked_withInferredType_typeInference() { + void test_tryRun_withInferredType_typeInference() { assertTrue( - Result.tryRunChecked(() -> TestMethods.voidMethodThrowingCustomException(new CustomException())) + Result.tryRun(() -> TestMethods.voidMethodThrowingCustomException(new CustomException())) .peek(aVoid -> fail("Result should be an error result")) - .peekError(customError -> customError.thisIsACustomError()) + .peekError(CustomException::thisIsACustomError) .isError() ); } @Test - void test_tryGetChecked_withInferredType_success() { + void test_tryGet_withInferredType_success() { val returned = "Hello world"; assertSame(returned, - Result.tryGetChecked(() -> TestMethods.stringMethodNotThrowingDeclaredIOException(returned)).unwrap() + Result.tryGet(() -> TestMethods.stringMethodNotThrowingDeclaredIOException(returned)).unwrap() ); } @Test - void test_tryGetChecked_withInferredType_error() { + void test_tryGet_withInferredType_error() { val thrown = new IOException("Expected exception"); assertSame(thrown, - Result.tryGetChecked(() -> TestMethods.stringMethodThrowingDeclaredIOException(thrown)).unwrapError() + Result.tryGet(() -> TestMethods.stringMethodThrowingDeclaredIOException(thrown)).unwrapError() ); } @Test - void test_tryGetChecked_withInferredType_rethrow() { + void test_tryGet_withInferredType_rethrow() { val thrown = new RuntimeException("You didn't expect it, did you?"); assertSame(thrown, - assertThrows(RuntimeException.class, () -> Result.tryGetChecked( + assertThrows(RuntimeException.class, () -> Result.tryGet( () -> TestMethods.stringMethodNotThrowingDeclaredIOExceptionButRuntimeException(thrown) )) ); } @Test - void test_tryGetChecked_withInferredType_typeInference() { + void test_tryGet_withInferredType_typeInference() { assertTrue( - Result.tryGetChecked(() -> TestMethods.stringMethodThrowingCustomException(new CustomException())) + Result.tryGet(() -> TestMethods.stringMethodThrowingCustomException(new CustomException())) .peek(string -> fail("Result should be an error result")) - .peekError(customError -> customError.thisIsACustomError()) + .peekError(CustomException::thisIsACustomError) .isError() ); } From 1e601af23690f57f337fc666d397d791a834077b Mon Sep 17 00:00:00 2001 From: Petr Portnov Date: Sun, 7 Nov 2021 14:58:29 +0300 Subject: [PATCH 6/6] feat(java-commons): add conditional mappers to `Result.Extensions` --- .../javacommons/object/Result.java | 95 ++++++++++++++++++- 1 file changed, 93 insertions(+), 2 deletions(-) diff --git a/java-commons/src/main/java/ru/progrm_jarvis/javacommons/object/Result.java b/java-commons/src/main/java/ru/progrm_jarvis/javacommons/object/Result.java index 85b67161..2c225234 100644 --- a/java-commons/src/main/java/ru/progrm_jarvis/javacommons/object/Result.java +++ b/java-commons/src/main/java/ru/progrm_jarvis/javacommons/object/Result.java @@ -18,6 +18,7 @@ import java.util.concurrent.Callable; import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Stream; @@ -1230,7 +1231,7 @@ public T rethrow( * of the ability to specify {@code super}-bounds on generic parameters relative to the other ones */ @SuppressWarnings("unchecked") // Results are immutable so they are always safe to upcast - public Result upcast(final @NotNull Result result) { + public <@Any T, @Any E> Result upcast(final @NotNull Result result) { return (Result) result; } @@ -1242,7 +1243,97 @@ public Result upcast(final @NotNull Result T any(final @NotNull Result result) { - return Extensions.upcast(result).orComputeDefault(Function.identity()); + return Extensions + .upcast(result) + .orComputeDefault(Function.identity()); + } + + /** + * Conditionally maps the {@link #unwrap() successful value} otherwise applying no changes. + * + * @param result given result + * @param predicate condition on which to apply the mapping to the {@link #unwrap() successful value} + * @param mappingFunction function used to map the {@link #unwrap() successful value} + * if it matches the predicate + * @param type of the successful result value + * @param type of the error result value + * @param type of the resulting successful value, super-type of {@code } + * @return the result whose {@link #unwrap() successful value} is mapped if it matches the predicate + */ + public @NotNull Result mapIf( + final @NonNull Result result, + final @NonNull Predicate predicate, + final @NonNull Function mappingFunction + ) { + return Extensions + .upcast(result) + .map(value -> predicate.test(value) ? mappingFunction.apply(value) : value); + } + + /** + * Conditionally maps the {@link #unwrap() successful value} otherwise applying no changes. + * + * @param result given result + * @param predicate condition on which to not apply the mapping to the {@link #unwrap() successful value} + * @param mappingFunction function used to map the {@link #unwrap() successful value} + * if it does not match the predicate + * @param type of the successful result value + * @param type of the error result value + * @param type of the resulting successful value, super-type of {@code } + * @return the result whose {@link #unwrap() successful value} is mapped if it does not match the predicate + */ + public @NotNull Result mapIfNot( + final @NonNull Result result, + final @NonNull Predicate predicate, + final @NonNull Function mappingFunction + ) { + return Extensions + .upcast(result) + .map(value -> predicate.test(value) ? value : mappingFunction.apply(value)); + } + + /** + * Conditionally maps the {@link #unwrapError() error value} otherwise applying no changes. + * + * @param result given result + * @param predicate condition on which to apply the mapping to the {@link #unwrapError() error value} + * @param mappingFunction function used to map the {@link #unwrapError() error value} + * if it matches the predicate + * @param type of the successful result value + * @param type of the error result value + * @param type of the resulting error value, super-type of {@code } + * @return the result whose {@link #unwrapError() error value} is mapped if it matches the predicate + */ + public @NotNull Result mapErrorIf( + final @NonNull Result result, + final @NonNull Predicate predicate, + final @NonNull Function mappingFunction + ) { + return Extensions + .upcast(result) + .mapError(error -> predicate.test(error) ? mappingFunction.apply(error) : error); + } + + /** + * Conditionally maps the {@link #unwrapError() error value} otherwise applying no changes. + * + * @param result given result + * @param predicate condition on which to not apply the mapping to the {@link #unwrapError() error value} + * @param mappingFunction function used to map the {@link #unwrapError() error value} + * if it does not match the predicate + * @param type of the successful result value + * @param type of the error result value + * @param type of the resulting error value, super-type of {@code } + * @return the result whose {@link #unwrapError() error value} is mapped if it does not match the predicate + */ + public @NotNull Result mapErrorIfNot( + final @NonNull Result result, + final @NonNull Predicate predicate, + final @NonNull Function mappingFunction + ) { + return Extensions + .upcast(result) + .mapError(error -> predicate.test(error) ? error : mappingFunction.apply(error)); } } }