All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.tonivade.purefun.concurrent.Future Maven / Gradle / Ivy

/*
 * Copyright (c) 2018-2019, Antonio Gabriel Muñoz Conejo 
 * Distributed under the terms of the MIT License
 */
package com.github.tonivade.purefun.concurrent;

import static com.github.tonivade.purefun.Function1.cons;
import static com.github.tonivade.purefun.Function1.identity;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.MILLISECONDS;

import java.time.Duration;
import java.util.NoSuchElementException;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import com.github.tonivade.purefun.CheckedProducer;
import com.github.tonivade.purefun.CheckedRunnable;
import com.github.tonivade.purefun.Consumer1;
import com.github.tonivade.purefun.Consumer2;
import com.github.tonivade.purefun.Function1;
import com.github.tonivade.purefun.HigherKind;
import com.github.tonivade.purefun.Matcher1;
import com.github.tonivade.purefun.Producer;
import com.github.tonivade.purefun.Unit;
import com.github.tonivade.purefun.type.Try;

@HigherKind
public interface Future {

  Executor DEFAULT_EXECUTOR = Executors.newCachedThreadPool();

  Try await();
  Try await(Duration timeout);

  void cancel(boolean mayInterruptThread);

  boolean isCompleted();
  boolean isCancelled();

  default boolean isSuccess() {
    return await().isSuccess();
  }

  default boolean isFailure() {
    return await().isFailure();
  }

  Future onSuccess(Consumer1 callback);
  Future onFailure(Consumer1 callback);
  Future onComplete(Consumer1> callback);

   Future map(Function1 mapper);

   Future flatMap(Function1> mapper);

   Future andThen(Future next);

  Future filter(Matcher1 matcher);

  default Future filterNot(Matcher1 matcher) {
    return filter(matcher.negate());
  }

  Future orElse(Future other);

  default T get() {
    return getOrElseThrow(NoSuchElementException::new);
  }

  default T getOrElse(T value) {
    return getOrElse(Producer.cons(value));
  }

  default T getOrElse(Producer value) {
    return await().getOrElse(value);
  }

  default  T getOrElseThrow(Producer producer) throws X {
    return await().getOrElseThrow(producer);
  }

  default Throwable getCause() {
    return await().getCause();
  }

  Future recover(Function1 mapper);

   Future recoverWith(Class type, Function1 mapper);

   Future fold(Function1 failureMapper, Function1 successMapper);

  default CompletableFuture toCompletableFuture() {
    CompletableFuture completableFuture = new CompletableFuture<>();
    onSuccess(completableFuture::complete);
    onFailure(completableFuture::completeExceptionally);
    return completableFuture;
  }

  Promise toPromise();

  FutureModule getModule();

  static  Future success(T value) {
    return success(DEFAULT_EXECUTOR, value);
  }

  static  Future success(Executor executor, T value) {
    return FutureImpl.sync(executor, () -> Try.success(value));
  }

  static  Future failure(Throwable error) {
    return failure(DEFAULT_EXECUTOR, error);
  }

  static  Future failure(Executor executor, Throwable error) {
    return FutureImpl.sync(executor, () -> Try.failure(error));
  }

  static  Future from(Callable callable) {
    return run(callable::call);
  }

  static  Future from(java.util.concurrent.Future future) {
    return run(future::get);
  }

  static  Future run(CheckedProducer task) {
    return run(DEFAULT_EXECUTOR, task);
  }

  static  Future run(Executor executor, CheckedProducer task) {
    return FutureImpl.async(executor, task.liftTry());
  }

  static Future exec(CheckedRunnable task) {
    return exec(DEFAULT_EXECUTOR, task);
  }

  static Future exec(Executor executor, CheckedRunnable task) {
    return run(executor, () -> { task.run(); return Unit.unit(); });
  }

  static  Future delay(Duration timeout, CheckedProducer producer) {
    return delay(DEFAULT_EXECUTOR, timeout, producer);
  }

  static  Future delay(Executor executor, Duration timeout, CheckedProducer producer) {
    return run(executor, () -> { MILLISECONDS.sleep(timeout.toMillis()); return producer.get(); });
  }

  static  Future defer(CheckedProducer> producer) {
    return defer(DEFAULT_EXECUTOR, producer);
  }

  static  Future defer(Executor executor, CheckedProducer> producer) {
    return run(executor, producer::get).flatMap(identity());
  }
}

interface FutureModule { }

final class FutureImpl implements Future {

  private final Executor executor;
  private final Promise promise = Promise.make();
  private final Cancellable cancellable = Cancellable.from(promise);

  private FutureImpl(Executor executor, Consumer2, Cancellable> task) {
    this.executor = requireNonNull(executor);
    requireNonNull(task).accept(promise, cancellable);
  }

  @Override
  public  Future map(Function1 mapper) {
    return transform(executor, this, value -> value.map(mapper));
  }

  @Override
  public  Future flatMap(Function1> mapper) {
    return chain(executor, this,
        value -> value.fold(Future::failure, mapper));
  }

  @Override
  public  Future andThen(Future next) {
    return flatMap(ignore -> next);
  }

  @Override
  public Future filter(Matcher1 matcher) {
    return transform(executor, this, value -> value.filter(matcher));
  }

  @Override
  public Future orElse(Future other) {
    return chain(executor, this, value -> value.fold(cons(other), Future::success));
  }

  @Override
  public Future recover(Function1 mapper) {
    return transform(executor, this, value -> value.recover(mapper));
  }

  @Override
  public  Future recoverWith(Class type, Function1 mapper) {
    return transform(executor, this, value -> value.recoverWith(type, mapper));
  }

  @Override
  public  Future fold(Function1 failureMapper, Function1 successMapper) {
    return transform(executor, this, value -> Try.success(value.fold(failureMapper, successMapper)));
  }

  @Override
  public Promise toPromise() {
    return promise;
  }

  @Override
  public Try await() {
    return promise.get();
  }

  @Override
  public Try await(Duration timeout) {
    return promise.get(timeout);
  }

  @Override
  public void cancel(boolean mayInterruptThread) {
    cancellable.cancel(mayInterruptThread);
  }

  @Override
  public boolean isCompleted() {
    return promise.isCompleted();
  }

  @Override
  public boolean isCancelled() {
    return cancellable.isCancelled();
  }

  @Override
  public Future onSuccess(Consumer1 callback) {
    promise.onComplete(value -> value.onSuccess(callback));
    return this;
  }

  @Override
  public Future onFailure(Consumer1 callback) {
    promise.onComplete(value -> value.onFailure(callback));
    return this;
  }

  @Override
  public Future onComplete(Consumer1> callback) {
    promise.onComplete(callback);
    return this;
  }

  @Override
  public FutureModule getModule() {
    throw new UnsupportedOperationException();
  }

  static  Future sync(Executor executor, Producer> producer) {
    return new FutureImpl<>(executor, (promise, cancel) -> promise.tryComplete(producer.get()));
  }

  static  Future transform(Executor executor, Future current, Function1, Try> mapper) {
    return new FutureImpl<>(executor,
        (promise, cancel) -> current.onComplete(value -> promise.tryComplete(mapper.apply(value))));
  }

  static  Future chain(Executor executor, Future current, Function1, Future> mapper) {
    return new FutureImpl<>(executor,
        (promise, cancel) -> current.onComplete(value -> mapper.apply(value).onComplete(promise::tryComplete)));
  }

  static  Future async(Executor executor, Producer> producer) {
    return new FutureImpl<>(executor,
        (promise, cancel) -> executor.execute(() -> { cancel.updateThread(); promise.tryComplete(producer.get()); }));
  }

  static  Future from(Executor executor, Promise promise) {
    return new FutureImpl<>(executor, (current, cancel) -> promise.onComplete(current::tryComplete));
  }
}