feat(commons): add switchsupport package for using switch instead of try/catch or Optional.*

This commit is contained in:
Johannes Frohnmeyer 2024-05-04 22:52:39 +02:00
parent f66a7b5656
commit 704e1987c4
Signed by: Johannes
GPG Key ID: E76429612C2929F4
6 changed files with 246 additions and 0 deletions

View File

@ -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<T> {
record Some<T>(T value) implements Opt<T> {
public static <T> Some<T> of(T value) {
return new Some<>(value);
}
}
record None<T>() implements Opt<T> {
public static <T> None<T> of() {
return new None<>();
}
}
static <T> Opt<T> empty() {
return None.of();
}
static <T> Opt<T> 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 <T> Type of the Optional
* @return Opt of the Optional
*/
static <T> Opt<T> over(Optional<T> 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<T> supplier) {
return switch (this) {
case Some(var value) -> value;
case None none -> supplier.get();
};
}
default <X extends Throwable> T getOrElseThrow(Supplier<X> exceptionSupplier) throws X {
return switch (this) {
case Some(var value) -> value;
case None none -> throw exceptionSupplier.get();
};
}
default <R> Opt<R> map(Function<T, R> mapper) {
return switch (this) {
case Some(var value) -> Some.of(mapper.apply(value));
case None none -> None.of();
};
}
default <R> Opt<R> flatMap(Function<T, Opt<R>> mapper) {
return switch (this) {
case Some(var value) -> mapper.apply(value);
case None none -> None.of();
};
}
default void ifPresent(Consumer<T> consumer) {
if (this instanceof Some(var value)) consumer.accept(value);
}
default void ifPresentOrElse(Consumer<T> consumer, Runnable runnable) {
switch (this) {
case Some(var value) -> consumer.accept(value);
case None none -> runnable.run();
}
}
default Opt<T> filter(Predicate<T> predicate) {
return switch (this) {
case Some(var value) when predicate.test(value) -> this;
case Some some -> None.of();
case None none -> this;
};
}
default Opt<T> or(Supplier<Opt<T>> supplier) {
return switch (this) {
case Some some -> this;
case None none -> supplier.get();
};
}
default Opt<T> orElse(Opt<T> other) {
return switch (this) {
case Some some -> this;
case None none -> other;
};
}
default T orElseGet(Supplier<T> supplier) {
return switch (this) {
case Some(var value) -> value;
case None none -> supplier.get();
};
}
default <X> Result<T, X> toResult(Supplier<X> errorSupplier) {
return switch (this) {
case Some(var value) -> Result.success(value);
case None none -> Result.failure(errorSupplier.get());
};
}
default Stream<T> stream() {
return switch (this) {
case Some(var value) -> Stream.of(value);
case None none -> Stream.empty();
};
}
}

View File

@ -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<T, X> {
record Success<T, X>(T value) implements Result<T, X> {
public static <T, X> Success<T, X> of(T value) {
return new Success<>(value);
}
}
record Failure<T, X>(X error) implements Result<T, X> {
public static <T, X> Failure<T, X> of(X error) {
return new Failure<>(error);
}
}
static <T, X> Result<T, X> of(Optional<T> value, Supplier<X> errorSupplier) {
return of(Opt.of(value), errorSupplier);
}
static <T, X> Result<T, X> of(Opt<T> value, Supplier<X> errorSupplier) {
return value.toResult(errorSupplier);
}
default Opt<T> toOpt() {
return switch (this) {
case Success(var value) -> Opt.of(value);
case Failure(var error) -> Opt.empty();
};
}
default <Y> Result<Y, X> map(Function<T, Y> mapper) {
return switch (this) {
case Success(var value) -> Result.success(mapper.apply(value));
case Failure(var error) -> Result.failure(error);
};
}
static <T, X> Result<T, X> success(T value) {
return Success.of(value);
}
static <T, X> Result<T, X> 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 + ")");
};
}
}

View File

@ -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<T, U, R, TEx extends Throwable> {
};
}
@Contract(value = "_ -> new", pure = true)
@ApiStatus.NonExtendable
default @NotNull BiFunction<T, U, Result<R, TEx>> toResult(@NotNull Class<TEx> 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.

View File

@ -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<T, R, TEx extends Throwable> {
};
}
@Contract(value = "_ -> new", pure = true)
@ApiStatus.NonExtendable
default @NotNull Function<T, Result<R, TEx>> toResult(@NotNull Class<TEx> 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.

View File

@ -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<T, TEx extends Throwable> {
};
}
@Contract(value = "_ -> new", pure = true)
@ApiStatus.NonExtendable
default @NotNull Supplier<Result<T, TEx>> toResult(@NotNull Class<TEx> 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.

View File

@ -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;
}