Skip to content

Commit

Permalink
feat(autoclosable): Add solveResource operation (#216)
Browse files Browse the repository at this point in the history
  • Loading branch information
JoseLion committed Nov 6, 2023
1 parent a6b0d4a commit 11e6b8f
Show file tree
Hide file tree
Showing 9 changed files with 298 additions and 91 deletions.
42 changes: 32 additions & 10 deletions src/main/java/io/github/joselion/maybe/Maybe.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package io.github.joselion.maybe;

import java.io.Closeable;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.function.Function;

import org.eclipse.jdt.annotation.Nullable;

import io.github.joselion.maybe.helpers.Common;
import io.github.joselion.maybe.util.function.ThrowingConsumer;
import io.github.joselion.maybe.util.function.ThrowingFunction;
import io.github.joselion.maybe.util.function.ThrowingRunnable;
Expand Down Expand Up @@ -97,8 +99,7 @@ public static <T, E extends Throwable> ResolveHandler<T, E> fromResolver(final T
try {
return ResolveHandler.ofSuccess(resolver.get());
} catch (Throwable e) { // NOSONAR
@SuppressWarnings("unchecked")
final var error = (E) e;
final var error = Common.<E>cast(e);
return ResolveHandler.ofError(error);
}
}
Expand All @@ -118,8 +119,7 @@ public static <E extends Throwable> EffectHandler<E> fromEffect(final ThrowingRu
effect.run();
return EffectHandler.empty();
} catch (Throwable e) { // NOSONAR
@SuppressWarnings("unchecked")
final var error = (E) e;
final var error = Common.<E>cast(e);
return EffectHandler.ofError(error);
}
}
Expand Down Expand Up @@ -201,6 +201,28 @@ public static <R extends AutoCloseable, E extends Throwable> ResourceHolder<R, E
return ResourceHolder.from(resource);
}

/**
* Prepare an {@link AutoCloseable} resource to use in a resolver or effect,
* using a {@link ThrowingSupplier}. Any exception thrown by the supplier
* will be propageted to the {@link ResourceHolder}. The resource will be
* automatically closed after the operation is finished, just like a common
* try-with-resources statement.
*
* @param <R> the type of the resource. Extends from {@link AutoCloseable}
* @param <E> the type of error the holder may have
* @param supplier the throwing supplier o the {@link AutoCloseable} resource
* @return a {@link ResourceHolder} which let's you choose to resolve a value
* or run an effect using the prepared resource
*/
public static <R extends Closeable, E extends Throwable> ResourceHolder<R, E> solveResource(
final ThrowingSupplier<R, E> supplier
) {
return Maybe
.fromResolver(supplier)
.map(ResourceHolder::<R, E>from)
.orElse(ResourceHolder::failure);
}

/**
* If present, maps the value to another using the provided mapper function.
* Otherwise, ignores the mapper and returns {@link #nothing()}.
Expand Down Expand Up @@ -246,14 +268,14 @@ public <U> Maybe<U> flatMap(final Function<T, Maybe<U>> mapper) {
* @return a {@link ResolveHandler} with either the resolved value, or the
* thrown exception to be handled
*/
@SuppressWarnings("unchecked")
public <U, E extends Throwable> ResolveHandler<U, E> resolve(final ThrowingFunction<T, U, E> resolver) {
try {
return value
.map(Maybe.partialResolver(resolver))
.orElseThrow();
} catch (final NoSuchElementException error) {
return ResolveHandler.ofError((E) error);
} catch (final NoSuchElementException e) {
final var error = Common.<E>cast(e);
return ResolveHandler.ofError(error);
}
}

Expand All @@ -266,14 +288,14 @@ public <U, E extends Throwable> ResolveHandler<U, E> resolve(final ThrowingFunct
* @return an {@link EffectHandler} with either the thrown exception to be
* handled or nothing
*/
@SuppressWarnings("unchecked")
public <E extends Throwable> EffectHandler<E> runEffect(final ThrowingConsumer<T, E> effect) {
try {
return value
.map(Maybe.partialEffect(effect))
.orElseThrow();
} catch (final NoSuchElementException error) {
return EffectHandler.ofError((E) error);
} catch (final NoSuchElementException e) {
final var error = Common.<E>cast(e);
return EffectHandler.ofError(error);
}
}

Expand Down
99 changes: 66 additions & 33 deletions src/main/java/io/github/joselion/maybe/ResolveHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import org.eclipse.jdt.annotation.Nullable;

import io.github.joselion.maybe.helpers.Common;
import io.github.joselion.maybe.util.Either;
import io.github.joselion.maybe.util.function.ThrowingConsumer;
import io.github.joselion.maybe.util.function.ThrowingFunction;
Expand Down Expand Up @@ -60,7 +61,7 @@ static <T, E extends Throwable> ResolveHandler<T, E> ofError(final E error) {
* @return the possible success value
*/
Optional<T> success() {
return value.rightToOptional();
return this.value.rightToOptional();
}

/**
Expand All @@ -69,7 +70,7 @@ Optional<T> success() {
* @return the possible thrown exception
*/
Optional<E> error() {
return value.leftToOptional();
return this.value.leftToOptional();
}

/**
Expand All @@ -80,7 +81,7 @@ Optional<E> error() {
* @return the same handler to continue chainning operations
*/
public ResolveHandler<T, E> doOnSuccess(final Consumer<T> effect) {
value.doOnRight(effect);
this.value.doOnRight(effect);

return this;
}
Expand All @@ -96,7 +97,7 @@ public ResolveHandler<T, E> doOnSuccess(final Consumer<T> effect) {
* @return the same handler to continue chainning operations
*/
public <X extends Throwable> ResolveHandler<T, E> doOnError(final Class<X> ofType, final Consumer<X> effect) {
value.leftToOptional()
this.value.leftToOptional()
.filter(ofType::isInstance)
.map(ofType::cast)
.ifPresent(effect);
Expand All @@ -112,7 +113,7 @@ public <X extends Throwable> ResolveHandler<T, E> doOnError(final Class<X> ofTyp
* @return the same handler to continue chainning operations
*/
public ResolveHandler<T, E> doOnError(final Consumer<E> effect) {
value.doOnLeft(effect);
this.value.doOnLeft(effect);

return this;
}
Expand All @@ -130,7 +131,7 @@ public ResolveHandler<T, E> doOnError(final Consumer<E> effect) {
* provided type was caught. The same handler instance otherwise
*/
public <X extends E> ResolveHandler<T, E> catchError(final Class<X> ofType, final Function<X, T> handler) {
return value
return this.value
.leftToOptional()
.filter(ofType::isInstance)
.map(ofType::cast)
Expand All @@ -149,7 +150,7 @@ public <X extends E> ResolveHandler<T, E> catchError(final Class<X> ofType, fina
* handler instance otherwise
*/
public ResolveHandler<T, E> catchError(final Function<E, T> handler) {
return value
return this.value
.mapLeft(handler)
.mapLeft(ResolveHandler::<T, E>ofSuccess)
.leftOrElse(this);
Expand All @@ -176,7 +177,7 @@ public <S, X extends Throwable> ResolveHandler<S, X> resolve(
final ThrowingFunction<T, S, X> onSuccess,
final ThrowingFunction<E, S, X> onError
) {
return value.unwrap(
return this.value.unwrap(
Maybe.partialResolver(onError),
Maybe.partialResolver(onSuccess)
);
Expand All @@ -192,12 +193,13 @@ public <S, X extends Throwable> ResolveHandler<S, X> resolve(
* resolves another
* @return a new handler with either the resolved value or an error
*/
@SuppressWarnings("unchecked")
public <S, X extends Throwable> ResolveHandler<S, X> resolve(final ThrowingFunction<T, S, X> resolver) {
return value.unwrap(
error -> ResolveHandler.ofError((X) error),
Maybe.partialResolver(resolver)
);
return this.value
.mapLeft(Common::<X>cast)
.unwrap(
ResolveHandler::ofError,
Maybe.partialResolver(resolver)
);
}

/**
Expand All @@ -214,7 +216,7 @@ public <X extends Throwable> EffectHandler<X> runEffect(
final ThrowingConsumer<T, X> onSuccess,
final ThrowingConsumer<E, X> onError
) {
return value.unwrap(
return this.value.unwrap(
Maybe.partialEffect(onError),
Maybe.partialEffect(onSuccess)
);
Expand All @@ -230,12 +232,13 @@ public <X extends Throwable> EffectHandler<X> runEffect(
* @return a new {@link EffectHandler} representing the result of the success
* callback or containg the error
*/
@SuppressWarnings("unchecked")
public <X extends Throwable> EffectHandler<X> runEffect(final ThrowingConsumer<T, X> effect) {
return value.unwrap(
error -> EffectHandler.ofError((X) error),
Maybe.partialEffect(effect)
);
return this.value
.mapLeft(Common::<X>cast)
.unwrap(
EffectHandler::ofError,
Maybe.partialEffect(effect)
);
}

/**
Expand All @@ -248,7 +251,7 @@ public <X extends Throwable> EffectHandler<X> runEffect(final ThrowingConsumer<T
* @return a new handler with either the mapped value, or the previous error
*/
public <U> ResolveHandler<U, E> map(final Function<T, U> mapper) {
return value
return this.value
.mapRight(mapper)
.unwrap(
ResolveHandler::ofError,
Expand All @@ -267,7 +270,7 @@ public <U> ResolveHandler<U, E> map(final Function<T, U> mapper) {
* error
*/
public <U> ResolveHandler<U, ClassCastException> cast(final Class<U> type) {
return value.unwrap(
return this.value.unwrap(
error -> ofError(new ClassCastException(error.getMessage())),
success -> {
try {
Expand All @@ -286,7 +289,7 @@ public <U> ResolveHandler<U, ClassCastException> cast(final Class<U> type) {
* @return the resolved value if present. Another value otherwise
*/
public T orElse(final T fallback) {
return value.rightOrElse(fallback);
return this.value.rightOrElse(fallback);
}

/**
Expand All @@ -299,7 +302,7 @@ public T orElse(final T fallback) {
* @return the resolved value if present. Another value otherwise
*/
public T orElse(final Function<E, T> mapper) {
return value.unwrap(mapper, Function.identity());
return this.value.unwrap(mapper, Function.identity());
}

/**
Expand All @@ -314,7 +317,7 @@ public T orElse(final Function<E, T> mapper) {
* @return the resolved value if present. Another value otherwise
*/
public T orElseGet(final Supplier<T> supplier) {
return value
return this.value
.rightToOptional()
.orElseGet(supplier);
}
Expand All @@ -332,7 +335,7 @@ public T orElseGet(final Supplier<T> supplier) {
* @return the resolved value if present. Just {@code null} otherwise.
*/
public @Nullable T orNull() {
return value.rightOrNull();
return this.value.rightOrNull();
}

/**
Expand All @@ -342,9 +345,9 @@ public T orElseGet(final Supplier<T> supplier) {
* @throws E the error thrown by the {@code resolve} operation
*/
public T orThrow() throws E {
return value
return this.value
.rightToOptional()
.orElseThrow(value::leftOrNull);
.orElseThrow(this.value::leftOrNull);
}

/**
Expand All @@ -357,9 +360,9 @@ public T orThrow() throws E {
* @throws X a mapped exception
*/
public <X extends Throwable> T orThrow(final Function<E, X> mapper) throws X {
return value
return this.value
.rightToOptional()
.orElseThrow(() -> mapper.apply(value.leftOrNull()));
.orElseThrow(() -> mapper.apply(this.value.leftOrNull()));
}

/**
Expand All @@ -369,7 +372,7 @@ public <X extends Throwable> T orThrow(final Function<E, X> mapper) throws X {
* @return the resolved value wrapped in a {@link Maybe} or holding the error
*/
public Maybe<T> toMaybe() {
return value
return this.value
.rightToOptional()
.map(Maybe::just)
.orElseGet(Maybe::nothing);
Expand All @@ -384,7 +387,7 @@ public Maybe<T> toMaybe() {
* {@code empty} optional otherwise.
*/
public Optional<T> toOptional() {
return value.rightToOptional();
return this.value.rightToOptional();
}

/**
Expand All @@ -399,7 +402,7 @@ public Optional<T> toOptional() {
* error on the left
*/
public Either<E, T> toEither() {
return value;
return this.value;
}

/**
Expand All @@ -418,11 +421,41 @@ public Either<E, T> toEither() {
* @see ResourceHolder#runEffectClosing(ThrowingConsumer)
*/
public <R extends AutoCloseable> ResourceHolder<R, E> mapToResource(final Function<T, R> mapper) {
return value
return this.value
.mapRight(mapper)
.unwrap(
ResourceHolder::failure,
ResourceHolder::from
);
}

/**
* Resolve a function that may create an {@link AutoCloseable} resource using
* the value in the handle, (if any). If the function is resolved it returns
* a {@link ResourceHolder} that will close the resource after used. If the
* function does not resolves or the value is not present, the error is
* propagated to the {@link ResourceHolder}.
*
* @param <R> the type of the {@link AutoCloseable} resource
* @param <X> the error type the solver function may throw
* @param solver a function that returns either a resource or throws an exception
* @return a {@link ResourceHolder} with the solved resource if the value is
* present or the error otherwise.
*/
public <R extends AutoCloseable, X extends Throwable> ResourceHolder<R, X> solveResource(
final ThrowingFunction<T, R, X> solver
) {
return this.value
.mapLeft(Common::<X>cast)
.unwrap(
ResourceHolder::failure,
prev ->
Maybe
.just(prev)
.resolve(solver)
.map(ResourceHolder::<R, X>from)
.catchError(ResourceHolder::failure)
.orElse(ResourceHolder::failure)
);
}
}
Loading

0 comments on commit 11e6b8f

Please sign in to comment.