package org.fungsi.concurrent;
import org.fungsi.Either;
import org.fungsi.Unit;
import org.fungsi.function.UnsafeFunction;
import org.fungsi.function.UnsafePredicate;
import java.time.Duration;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
public interface Future<T> {
public static <T> Future<T> constant(Either<T, Throwable> e) { return new ConstFuture<>(e); }
@SuppressWarnings("unchecked")
public static <T> Future<T> never() { return (Future<T>) NEVER; }
T get();
T get(Duration timeout);
Optional<Either<T, Throwable>> poll();
void respond(Consumer<Either<T, Throwable>> fn);
<TT> Future<TT> transform(Function<Either<T, Throwable>, Future<TT>> fn);
default <TT> Future<TT> bind(UnsafeFunction<T, Future<TT>> fn) {
return transform(e -> e.fold(Futures.safe(fn), Futures::failure));
}
default boolean isDone() {
return poll().isPresent();
}
default boolean isSuccess() {
return isDone() && poll().get().isLeft();
}
default boolean isFailure() {
return isDone() && poll().get().isRight();
}
default <TT> Future<TT> flatMap(UnsafeFunction<T, Future<TT>> fn) { return bind(fn); }
default <TT> Future<TT> map(UnsafeFunction<T, TT> fn) {
return bind(fn.eitherFunction().andThen(Future::constant));
}
default Future<T> filter(UnsafePredicate<T> fn) {
return bind(o -> fn.test(o) ? Future.this : never());
}
default Future<T> then(Future<T> other) {
return bind(it -> other);
}
default Future<Unit> toUnit() {
return bind(it -> Futures.unit());
}
default Future<T> pipeTo(Promise<T> p) {
respond(p::set);
return this;
}
default Future<T> within(Duration d, Timer timer) {
Promise<T> p = Promises.create();
timer.schedule(d, () -> p.set(Either.failure(new TimeoutException())));
pipeTo(p);
return p;
}
default Future<T> onSuccess(Consumer<T> fn) {
respond(e -> e.ifLeft(fn));
return this;
}
default Future<T> onFailure(Consumer<Throwable> fn) {
respond(e -> e.ifRight(fn));
return this;
}
default Future<T> mayRescue(UnsafeFunction<Throwable, Future<T>> fn) {
return transform(e -> e.fold(Futures::success, Futures.safe(fn)));
}
default Future<T> rescue(UnsafeFunction<Throwable, T> fn) {
return mayRescue(fn.eitherFunction().andThen(Future::constant));
}
static final class ConstFuture<T> implements Future<T> {
private final Either<T, Throwable> e;
public ConstFuture(Either<T, Throwable> e) {
this.e = e;
}
@Override
public Optional<Either<T, Throwable>> poll() {
return Optional.of(e);
}
@Override
public T get() {
return Either.unsafe(e);
}
@Override
public T get(Duration timeout) {
return get();
}
@Override
public void respond(Consumer<Either<T, Throwable>> fn) {
fn.accept(e);
}
@Override
public <TT> Future<TT> transform(Function<Either<T, Throwable>, Future<TT>> fn) {
return fn.apply(e);
}
}
static final Future<Object> NEVER = new Future<Object>() {
@SuppressWarnings("unchecked")
private <TT> Future<TT> self() { return (Future<TT>) this; }
@Override
public Optional<Either<Object, Throwable>> poll() {
return Optional.empty();
}
@Override
public Object get() {
throw new IllegalStateException();
}
@Override
public Object get(Duration timeout) {
throw new TimeoutException();
}
@Override
public boolean isDone() {
return false;
}
@Override
public boolean isSuccess() {
return false;
}
@Override
public boolean isFailure() {
return false;
}
@Override
public void respond(Consumer<Either<Object, Throwable>> fn) {
fn.accept(Either.right(new TimeoutException()));
}
@Override
public <TT> Future<TT> transform(Function<Either<Object, Throwable>, Future<TT>> fn) {
return fn.apply(Either.right(new TimeoutException()));
}
@Override
public <TT> Future<TT> bind(UnsafeFunction<Object, Future<TT>> fn) {
return self();
}
};
}