From 704e1987c4b12832e82f3aae324ab13a244b751c Mon Sep 17 00:00:00 2001 From: JFronny Date: Sat, 4 May 2024 22:52:39 +0200 Subject: [PATCH] feat(commons): add switchsupport package for using switch instead of try/catch or Optional.* --- .../jfronny/commons/switchsupport/Opt.java | 138 ++++++++++++++++++ .../jfronny/commons/switchsupport/Result.java | 65 +++++++++ .../commons/throwable/ThrowingBiFunction.java | 14 ++ .../commons/throwable/ThrowingFunction.java | 14 ++ .../commons/throwable/ThrowingSupplier.java | 14 ++ commons/src/main/java/module-info.java | 1 + 6 files changed, 246 insertions(+) create mode 100644 commons/src/main/java/io/gitlab/jfronny/commons/switchsupport/Opt.java create mode 100644 commons/src/main/java/io/gitlab/jfronny/commons/switchsupport/Result.java diff --git a/commons/src/main/java/io/gitlab/jfronny/commons/switchsupport/Opt.java b/commons/src/main/java/io/gitlab/jfronny/commons/switchsupport/Opt.java new file mode 100644 index 0000000..edbc1d1 --- /dev/null +++ b/commons/src/main/java/io/gitlab/jfronny/commons/switchsupport/Opt.java @@ -0,0 +1,138 @@ +package io.gitlab.jfronny.commons.switchsupport; + +import java.util.Optional; +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; + +public sealed interface Opt { + record Some(T value) implements Opt { + public static Some of(T value) { + return new Some<>(value); + } + } + + record None() implements Opt { + public static None of() { + return new None<>(); + } + } + + static Opt empty() { + return None.of(); + } + + static Opt of(T value) { + return value == null ? None.of() : Some.of(value); + } + + /** + * Converts an Optional to an Opt. + * Useful as a static import for switching over Optionals. + * + * @param optional Optional to convert to Opt + * @param Type of the Optional + * @return Opt of the Optional + */ + static Opt over(Optional optional) { + return optional.map(Opt::of).orElseGet(Opt::empty); + } + + default T get() { + return switch (this) { + case Some(var value) -> value; + case None none -> throw new IllegalStateException("Opt is None"); + }; + } + + default T getOrElse(T defaultValue) { + return switch (this) { + case Some(var value) -> value; + case None none -> defaultValue; + }; + } + + default T getOrElseGet(Supplier supplier) { + return switch (this) { + case Some(var value) -> value; + case None none -> supplier.get(); + }; + } + + default T getOrElseThrow(Supplier exceptionSupplier) throws X { + return switch (this) { + case Some(var value) -> value; + case None none -> throw exceptionSupplier.get(); + }; + } + + default Opt map(Function mapper) { + return switch (this) { + case Some(var value) -> Some.of(mapper.apply(value)); + case None none -> None.of(); + }; + } + + default Opt flatMap(Function> mapper) { + return switch (this) { + case Some(var value) -> mapper.apply(value); + case None none -> None.of(); + }; + } + + default void ifPresent(Consumer consumer) { + if (this instanceof Some(var value)) consumer.accept(value); + } + + default void ifPresentOrElse(Consumer consumer, Runnable runnable) { + switch (this) { + case Some(var value) -> consumer.accept(value); + case None none -> runnable.run(); + } + } + + default Opt filter(Predicate predicate) { + return switch (this) { + case Some(var value) when predicate.test(value) -> this; + case Some some -> None.of(); + case None none -> this; + }; + } + + default Opt or(Supplier> supplier) { + return switch (this) { + case Some some -> this; + case None none -> supplier.get(); + }; + } + + default Opt orElse(Opt other) { + return switch (this) { + case Some some -> this; + case None none -> other; + }; + } + + default T orElseGet(Supplier supplier) { + return switch (this) { + case Some(var value) -> value; + case None none -> supplier.get(); + }; + } + + default Result toResult(Supplier errorSupplier) { + return switch (this) { + case Some(var value) -> Result.success(value); + case None none -> Result.failure(errorSupplier.get()); + }; + } + + default Stream stream() { + return switch (this) { + case Some(var value) -> Stream.of(value); + case None none -> Stream.empty(); + }; + } +} diff --git a/commons/src/main/java/io/gitlab/jfronny/commons/switchsupport/Result.java b/commons/src/main/java/io/gitlab/jfronny/commons/switchsupport/Result.java new file mode 100644 index 0000000..967c41a --- /dev/null +++ b/commons/src/main/java/io/gitlab/jfronny/commons/switchsupport/Result.java @@ -0,0 +1,65 @@ +package io.gitlab.jfronny.commons.switchsupport; + +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Supplier; + +public sealed interface Result { + record Success(T value) implements Result { + public static Success of(T value) { + return new Success<>(value); + } + } + + record Failure(X error) implements Result { + public static Failure of(X error) { + return new Failure<>(error); + } + } + + static Result of(Optional value, Supplier errorSupplier) { + return of(Opt.of(value), errorSupplier); + } + + static Result of(Opt value, Supplier errorSupplier) { + return value.toResult(errorSupplier); + } + + default Opt toOpt() { + return switch (this) { + case Success(var value) -> Opt.of(value); + case Failure(var error) -> Opt.empty(); + }; + } + + default Result map(Function mapper) { + return switch (this) { + case Success(var value) -> Result.success(mapper.apply(value)); + case Failure(var error) -> Result.failure(error); + }; + } + + static Result success(T value) { + return Success.of(value); + } + + static Result failure(X error) { + return Failure.of(error); + } + + default T get() { + return switch (this) { + case Success(var value) -> value; + case Failure(Throwable error) -> throw new IllegalStateException("Result is Failure", error); + case Failure(var error) -> throw new IllegalStateException("Result is Failure (" + error + ")"); + }; + } + + default Throwable getError() { + return switch (this) { + case Success s -> throw new IllegalStateException("Result is Success"); + case Failure(Throwable error) -> error; + case Failure(var error) -> new IllegalStateException("Result is Failure (" + error + ")"); + }; + } +} diff --git a/commons/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingBiFunction.java b/commons/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingBiFunction.java index 373bae0..a8dc929 100644 --- a/commons/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingBiFunction.java +++ b/commons/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingBiFunction.java @@ -1,5 +1,6 @@ package io.gitlab.jfronny.commons.throwable; +import io.gitlab.jfronny.commons.switchsupport.Result; import io.gitlab.jfronny.commons.tuple.Tuple; import org.jetbrains.annotations.*; @@ -113,6 +114,19 @@ public interface ThrowingBiFunction { }; } + @Contract(value = "_ -> new", pure = true) + @ApiStatus.NonExtendable + default @NotNull BiFunction> toResult(@NotNull Class exception) { + return (v, u) -> { + try { + return Result.success(this.apply(v, u)); + } catch (Throwable e) { + if (exception.isAssignableFrom(e.getClass())) return Result.failure((TEx) e); + else throw ExceptionWrapper.wrap(e); + } + }; + } + /** * Hides the exception this could throw from the java compiler, effectively making it unchecked. * Wraps the function in a function not marked to throw the checked exception. diff --git a/commons/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingFunction.java b/commons/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingFunction.java index 0157c10..be524ec 100644 --- a/commons/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingFunction.java +++ b/commons/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingFunction.java @@ -1,5 +1,6 @@ package io.gitlab.jfronny.commons.throwable; +import io.gitlab.jfronny.commons.switchsupport.Result; import org.jetbrains.annotations.*; import java.util.Objects; @@ -89,6 +90,19 @@ public interface ThrowingFunction { }; } + @Contract(value = "_ -> new", pure = true) + @ApiStatus.NonExtendable + default @NotNull Function> toResult(@NotNull Class exception) { + return v -> { + try { + return Result.success(this.apply(v)); + } catch (Throwable e) { + if (exception.isAssignableFrom(e.getClass())) return Result.failure((TEx) e); + else throw ExceptionWrapper.wrap(e); + } + }; + } + /** * Hides the exception this could throw from the java compiler, effectively making it unchecked. * Wraps the function in a function not marked to throw the checked exception. diff --git a/commons/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingSupplier.java b/commons/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingSupplier.java index 1d4bd9d..eb2832f 100644 --- a/commons/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingSupplier.java +++ b/commons/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingSupplier.java @@ -1,5 +1,6 @@ package io.gitlab.jfronny.commons.throwable; +import io.gitlab.jfronny.commons.switchsupport.Result; import org.jetbrains.annotations.*; import java.util.Objects; @@ -76,6 +77,19 @@ public interface ThrowingSupplier { }; } + @Contract(value = "_ -> new", pure = true) + @ApiStatus.NonExtendable + default @NotNull Supplier> toResult(@NotNull Class exception) { + return () -> { + try { + return Result.success(this.get()); + } catch (Throwable e) { + if (exception.isAssignableFrom(e.getClass())) return Result.failure((TEx) e); + else throw ExceptionWrapper.wrap(e); + } + }; + } + /** * Hides the exception this could throw from the java compiler, effectively making it unchecked. * Wraps the supplier in a supplier not marked to throw the checked exception. diff --git a/commons/src/main/java/module-info.java b/commons/src/main/java/module-info.java index fe29f84..513d24b 100644 --- a/commons/src/main/java/module-info.java +++ b/commons/src/main/java/module-info.java @@ -9,4 +9,5 @@ module io.gitlab.jfronny.commons { exports io.gitlab.jfronny.commons.ref; exports io.gitlab.jfronny.commons.throwable; exports io.gitlab.jfronny.commons.tuple; + exports io.gitlab.jfronny.commons.switchsupport; } \ No newline at end of file