Skip to content

Commit

Permalink
fix(core): Ensure Maybe operations are safe (#219)
Browse files Browse the repository at this point in the history
  • Loading branch information
JoseLion committed Nov 8, 2023
1 parent 82a79ee commit 37500b1
Show file tree
Hide file tree
Showing 13 changed files with 1,420 additions and 1,409 deletions.
36 changes: 20 additions & 16 deletions src/main/java/io/github/joselion/maybe/EffectHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ static <E extends Throwable> EffectHandler<E> ofError(final E error) {
* @return the possible thrown exception
*/
Optional<E> error() {
return error;
return this.error;
}

/**
Expand All @@ -65,7 +65,7 @@ Optional<E> error() {
* @return the same handler to continue chainning operations
*/
public EffectHandler<E> doOnSuccess(final Runnable effect) {
if (error.isEmpty()) {
if (this.error.isEmpty()) {
effect.run();
}

Expand All @@ -83,7 +83,8 @@ public EffectHandler<E> doOnSuccess(final Runnable effect) {
* @return the same handler to continue chainning operations
*/
public <X extends Throwable> EffectHandler<E> doOnError(final Class<X> ofType, final Consumer<X> effect) {
error.filter(ofType::isInstance)
this.error
.filter(ofType::isInstance)
.map(ofType::cast)
.ifPresent(effect);

Expand All @@ -98,7 +99,7 @@ public <X extends Throwable> EffectHandler<E> doOnError(final Class<X> ofType, f
* @return the same handler to continue chainning operations
*/
public EffectHandler<E> doOnError(final Consumer<E> effect) {
error.ifPresent(effect);
this.error.ifPresent(effect);

return this;
}
Expand All @@ -116,7 +117,8 @@ public EffectHandler<E> doOnError(final Consumer<E> effect) {
* caught. The same handler instance otherwise
*/
public <X extends E> EffectHandler<E> catchError(final Class<X> ofType, final Consumer<X> handler) {
return error.filter(ofType::isInstance)
return this.error
.filter(ofType::isInstance)
.map(ofType::cast)
.map(caught -> {
handler.accept(caught);
Expand All @@ -136,11 +138,12 @@ public <X extends E> EffectHandler<E> catchError(final Class<X> ofType, final Co
* instance otherwise
*/
public EffectHandler<E> catchError(final Consumer<E> handler) {
return error.map(caught -> {
handler.accept(caught);
return EffectHandler.<E>empty();
})
.orElse(this);
return this.error
.map(caught -> {
handler.accept(caught);
return EffectHandler.<E>empty();
})
.orElse(this);
}

/**
Expand All @@ -157,7 +160,8 @@ public <X extends Throwable> EffectHandler<X> runEffect(
final ThrowingRunnable<X> onSuccess,
final ThrowingConsumer<E, X> onError
) {
return error.map(Maybe.partialEffect(onError))
return this.error
.map(Maybe.partialEffect(onError))
.orElseGet(() -> Maybe.fromEffect(onSuccess));
}

Expand All @@ -182,7 +186,7 @@ public <X extends Throwable> EffectHandler<X> runEffect(final ThrowingRunnable<X
* @param effect a consumer function that receives the caught error
*/
public void orElse(final Consumer<E> effect) {
error.ifPresent(effect);
this.error.ifPresent(effect);
}

/**
Expand All @@ -191,8 +195,8 @@ public void orElse(final Consumer<E> effect) {
* @throws E the error thrown by the {@code effect} operation
*/
public void orThrow() throws E {
if (error.isPresent()) {
throw error.get();
if (this.error.isPresent()) {
throw this.error.get();
}
}

Expand All @@ -205,8 +209,8 @@ public void orThrow() throws E {
* @throws X a mapped exception
*/
public <X extends Throwable> void orThrow(final Function<E, X> mapper) throws X {
if (error.isPresent()) {
throw mapper.apply(error.get());
if (this.error.isPresent()) {
throw mapper.apply(this.error.get());
}
}
}
55 changes: 29 additions & 26 deletions src/main/java/io/github/joselion/maybe/Maybe.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import org.eclipse.jdt.annotation.Nullable;

import io.github.joselion.maybe.helpers.Common;
import io.github.joselion.maybe.helpers.Commons;
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 @@ -37,7 +37,7 @@ private Maybe(final @Nullable T value) {
* @return the possible wrapped value
*/
Optional<T> value() {
return value;
return this.value;
}

/**
Expand Down Expand Up @@ -81,7 +81,9 @@ public static <T> Maybe<T> nothing() {
* {@link #nothing()} otherwise
*/
public static <T> Maybe<T> fromOptional(final Optional<T> value) {
return new Maybe<>(value.orElse(null));
return value
.map(Maybe::new)
.orElseGet(Maybe::nothing);
}

/**
Expand All @@ -99,7 +101,7 @@ public static <T, E extends Throwable> ResolveHandler<T, E> fromResolver(final T
try {
return ResolveHandler.ofSuccess(resolver.get());
} catch (Throwable e) { // NOSONAR
final var error = Common.<E>cast(e);
final var error = Commons.<E>cast(e);
return ResolveHandler.ofError(error);
}
}
Expand All @@ -119,7 +121,7 @@ public static <E extends Throwable> EffectHandler<E> fromEffect(final ThrowingRu
effect.run();
return EffectHandler.empty();
} catch (Throwable e) { // NOSONAR
final var error = Common.<E>cast(e);
final var error = Commons.<E>cast(e);
return EffectHandler.ofError(error);
}
}
Expand Down Expand Up @@ -233,9 +235,10 @@ public static <R extends Closeable, E extends Throwable> ResourceHolder<R, E> so
* {@link #nothing()} otherwise
*/
public <U> Maybe<U> map(final Function<T, U> mapper) {
return value.map(mapper)
.map(Maybe::just)
.orElseGet(Maybe::nothing);
return Maybe
.fromOptional(this.value)
.resolve(mapper::apply)
.toMaybe();
}

/**
Expand All @@ -252,7 +255,9 @@ public <U> Maybe<U> map(final Function<T, U> mapper) {
* {@link #nothing()} otherwise
*/
public <U> Maybe<U> flatMap(final Function<T, Maybe<U>> mapper) {
return value.map(mapper)
return Maybe
.fromOptional(this.value)
.resolve(mapper::apply)
.orElseGet(Maybe::nothing);
}

Expand All @@ -270,11 +275,11 @@ public <U> Maybe<U> flatMap(final Function<T, Maybe<U>> mapper) {
*/
public <U, E extends Throwable> ResolveHandler<U, E> resolve(final ThrowingFunction<T, U, E> resolver) {
try {
return value
return this.value
.map(Maybe.partialResolver(resolver))
.orElseThrow();
} catch (final NoSuchElementException e) {
final var error = Common.<E>cast(e);
final var error = Commons.<E>cast(e);
return ResolveHandler.ofError(error);
}
}
Expand All @@ -290,11 +295,11 @@ public <U, E extends Throwable> ResolveHandler<U, E> resolve(final ThrowingFunct
*/
public <E extends Throwable> EffectHandler<E> runEffect(final ThrowingConsumer<T, E> effect) {
try {
return value
return this.value
.map(Maybe.partialEffect(effect))
.orElseThrow();
} catch (final NoSuchElementException e) {
final var error = Common.<E>cast(e);
final var error = Commons.<E>cast(e);
return EffectHandler.ofError(error);
}
}
Expand All @@ -309,12 +314,10 @@ public <E extends Throwable> EffectHandler<E> runEffect(final ThrowingConsumer<T
* {@link #nothing()} otherwise
*/
public <U> Maybe<U> cast(final Class<U> type) {
try {
final var newValue = type.cast(value.orElseThrow());
return Maybe.just(newValue);
} catch (final ClassCastException error) {
return nothing();
}
return Maybe
.fromOptional(this.value)
.resolve(type::cast)
.toMaybe();
}

/**
Expand All @@ -323,7 +326,7 @@ public <U> Maybe<U> cast(final Class<U> type) {
* @return true if the value is present, false otherwise
*/
public boolean hasValue() {
return value.isPresent();
return this.value.isPresent();
}

/**
Expand All @@ -332,7 +335,7 @@ public boolean hasValue() {
* @return true if the value is NOT present, false otherwise
*/
public boolean hasNothing() {
return value.isEmpty();
return this.value.isEmpty();
}

/**
Expand All @@ -342,7 +345,7 @@ public boolean hasNothing() {
* @return an optional with the value, if preset. An empty optional otherwise
*/
public Optional<T> toOptional() {
return value;
return this.value;
}

/**
Expand All @@ -365,7 +368,7 @@ public boolean equals(final Object obj) {

if (obj instanceof Maybe<?>) {
final var other = (Maybe<?>) obj;
return other.toOptional().equals(value);
return other.toOptional().equals(this.value);
}

return false;
Expand All @@ -379,7 +382,7 @@ public boolean equals(final Object obj) {
*/
@Override
public int hashCode() {
return value.hashCode();
return this.value.hashCode();
}

/**
Expand All @@ -391,9 +394,9 @@ public int hashCode() {
*/
@Override
public String toString() {
return value
return this.value
.map(Object::toString)
.map(it -> String.format("Maybe[%s]", it))
.map(x -> String.format("Maybe[%s]", x))
.orElse("Maybe.nothing");
}
}
11 changes: 6 additions & 5 deletions src/main/java/io/github/joselion/maybe/ResolveHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import org.eclipse.jdt.annotation.Nullable;

import io.github.joselion.maybe.helpers.Common;
import io.github.joselion.maybe.helpers.Commons;
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 @@ -97,7 +97,8 @@ 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) {
this.value.leftToOptional()
this.value
.leftToOptional()
.filter(ofType::isInstance)
.map(ofType::cast)
.ifPresent(effect);
Expand Down Expand Up @@ -195,7 +196,7 @@ public <S, X extends Throwable> ResolveHandler<S, X> resolve(
*/
public <S, X extends Throwable> ResolveHandler<S, X> resolve(final ThrowingFunction<T, S, X> resolver) {
return this.value
.mapLeft(Common::<X>cast)
.mapLeft(Commons::<X>cast)
.unwrap(
ResolveHandler::ofError,
Maybe.partialResolver(resolver)
Expand Down Expand Up @@ -234,7 +235,7 @@ public <X extends Throwable> EffectHandler<X> runEffect(
*/
public <X extends Throwable> EffectHandler<X> runEffect(final ThrowingConsumer<T, X> effect) {
return this.value
.mapLeft(Common::<X>cast)
.mapLeft(Commons::<X>cast)
.unwrap(
EffectHandler::ofError,
Maybe.partialEffect(effect)
Expand Down Expand Up @@ -446,7 +447,7 @@ public <R extends AutoCloseable, X extends Throwable> ResourceHolder<R, X> solve
final ThrowingFunction<T, R, X> solver
) {
return this.value
.mapLeft(Common::<X>cast)
.mapLeft(Commons::<X>cast)
.unwrap(
ResourceHolder::failure,
prev ->
Expand Down
18 changes: 9 additions & 9 deletions src/main/java/io/github/joselion/maybe/ResourceHolder.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import java.util.Optional;

import io.github.joselion.maybe.helpers.Common;
import io.github.joselion.maybe.helpers.Commons;
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 @@ -61,7 +61,7 @@ static <R extends AutoCloseable, E extends Throwable> ResourceHolder<R, E> failu
* @return the possible stored resource
*/
Optional<R> resource() {
return value.rightToOptional();
return this.value.rightToOptional();
}

/**
Expand All @@ -70,7 +70,7 @@ Optional<R> resource() {
* @return the possible propagated error
*/
Optional<E> error() {
return value.leftToOptional();
return this.value.leftToOptional();
}

/**
Expand All @@ -91,15 +91,15 @@ Optional<E> error() {
* exception to be handled
*/
public <T, X extends Throwable> ResolveHandler<T, X> resolveClosing(final ThrowingFunction<R, T, X> resolver) {
return value
.mapLeft(Common::<X>cast)
return this.value
.mapLeft(Commons::<X>cast)
.unwrap(
ResolveHandler::ofError,
resource -> {
try (var res = resource) {
return ResolveHandler.ofSuccess(resolver.apply(res));
} catch (final Throwable e) { //NOSONAR
final var error = Common.<X>cast(e);
final var error = Commons.<X>cast(e);
return ResolveHandler.ofError(error);
}
}
Expand All @@ -123,16 +123,16 @@ public <T, X extends Throwable> ResolveHandler<T, X> resolveClosing(final Throwi
* handled or nothing
*/
public <X extends Throwable> EffectHandler<X> runEffectClosing(final ThrowingConsumer<R, X> effect) {
return value
.mapLeft(Common::<X>cast)
return this.value
.mapLeft(Commons::<X>cast)
.unwrap(
EffectHandler::ofError,
resource -> {
try (var res = resource) {
effect.accept(res);
return EffectHandler.empty();
} catch (final Throwable e) { // NOSONAR
final var error = Common.<X>cast(e);
final var error = Commons.<X>cast(e);
return EffectHandler.ofError(error);
}
}
Expand Down
Loading

0 comments on commit 37500b1

Please sign in to comment.