package org.fungsi.concurrent;
import com.google.common.collect.ImmutableList;
import org.fungsi.Either;
import org.fungsi.Unit;
import org.fungsi.function.UnsafeFunction;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Collectors;
public final class Futures {
private Futures() {}
public static Future<Unit> unit() {
return Future.constant(Unit.left());
}
public static <T> Future<T> success(T value) {
return Future.constant(Either.success(value));
}
public static <T> Future<T> failure(Throwable cause) {
return Future.constant(Either.failure(cause));
}
public static <T> Future<T> flatten(Either<Future<T>, Throwable> e) {
return e.fold(Function.identity(), Futures::failure);
}
public static <T, R> Function<T, Future<R>> safe(UnsafeFunction<T, Future<R>> fn) {
return fn.safeFunction().andThen(Futures::flatten);
}
@SuppressWarnings("unchecked")
public static <T> Future<List<T>> collect(List<Future<T>> futures) {
if (futures.isEmpty()) {
return Futures.success(ImmutableList.of());
}
if (futures.size() == 1) {
return futures.get(0).map(ImmutableList::of);
}
final Promise<List<T>> promise = Promises.create();
final Object[] lock = new Object[0];
final Object[] result = new Object[futures.size()];
final int[] index = {0};
for (Future<T> future : futures) {
future
.onFailure(promise::fail)
.onSuccess(res -> {
if (promise.isDone()) return;
synchronized (lock) {
result[index[0]++] = res;
if (index[0] >= result.length) {
promise.complete(Arrays.asList((T[]) result));
}
}
})
;
}
return promise;
}
public static <T> Collector<Future<T>, ?, Future<List<T>>> collect() {
return Collectors.collectingAndThen(Collectors.toList(), Futures::collect);
}
}