content: package com.github.aesteve.vertx.nubes.utils.async;
  
  import io.vertx.core.AsyncResult;
  import io.vertx.core.Future;
  import io.vertx.core.Handler;
  import io.vertx.core.logging.Logger;
  import io.vertx.ext.web.RoutingContext;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.List;
  
  public interface AsyncUtils {
  
    static <T> Handler<AsyncResult<T>> completeFinally(Future<T> fut) {
      return (res -> fut.complete());
    }
  
    static <T> Handler<AsyncResult<T>> completeOrFail(Future<T> fut) {
      return res -> {
        if (res.failed()) {
          fut.fail(res.cause());
        } else {
          fut.complete(res.result());
        }
      };
    }
  
    static <T> Handler<AsyncResult<T>> ignoreResult(Future<Void> future) {
      return res -> {
        if (res.failed()) {
          future.fail(res.cause());
        } else {
          future.complete();
        }
      };
    }
  
    static <T> AsyncResult<Void> withoutResult(AsyncResult<T> res) {
      if (res.failed()) {
        return Future.failedFuture(res.cause());
      } else {
        return Future.succeededFuture();
      }
    }
  
    static <T> Handler<AsyncResult<T>> onSuccessOnly(Future<Void> future, Handler<T> handler) {
      return res -> {
        if (res.failed()) {
          future.fail(res.cause());
          return;
        }
        handler.handle(res.result());
      };
    }
  
    static <T> Handler<AsyncResult<T>> onSuccessOnly(NoArgHandler block) {
      return res -> {
        if (res.failed()) {
          return;
        }
        block.handle();
      };
    }
  
    static <T> Handler<AsyncResult<T>> onSuccessOnly(Handler<T> handler) {
      return res -> {
        if (res.failed()) {
          return;
        }
        handler.handle(res.result());
      };
    }
  
    static <T> Handler<AsyncResult<T>> onFailureOnly(Handler<T> handler) {
      return res -> {
        if (res.failed()) {
          handler.handle(res.result());
        }
      };
    }
  
    static <T> Handler<AsyncResult<T>> nextOrFail(RoutingContext context) {
      return res -> {
        if (res.failed()) {
          context.fail(res.cause());
        } else {
          context.next();
        }
      };
    }
  
    static <T> Handler<AsyncResult<T>> failOr(RoutingContext context, Handler<AsyncResult<T>> handler) {
      return res -> {
        if (res.failed()) {
          context.fail(res.cause());
        } else {
          handler.handle(res);
        }
      };
    }
  
    static <T, U, V> Future<V> chainOnSuccess(Handler<AsyncResult<T>> globalHandler, Future<U> future, Handler<Future<V>> nextHandler) {
      Future<V> nextFuture = Future.future();
      future.setHandler(res -> {
        if (res.failed()) {
          globalHandler.handle(Future.failedFuture(res.cause()));
        } else {
          nextHandler.handle(nextFuture);
        }
      });
      return nextFuture;
    }
  
    static <T> void chainHandlers(Handler<AsyncResult<T>> global, List<Handler<Future<T>>> handlers) {
      if (handlers == null || handlers.isEmpty()) {
        global.handle(Future.succeededFuture());
        return;
      }
      int nbHandlers = handlers.size();
      Future<T> firstFuture = Future.future();
      if (nbHandlers == 1) {
        firstFuture.setHandler(global);
        handlers.get(0).handle(firstFuture);
        return;
      }
      List<Future<T>> futures = new ArrayList<>(handlers.size());
      futures.add(firstFuture);
      int i = 0;
      for (Handler<Future<T>> handler : handlers) {
        if (i > 0) {
          Future<T> fut = futures.get(i - 1);
          futures.add(chainOnSuccess(global, fut, handler));
        }
        i++;
      }
      futures.get(futures.size() - 1).setHandler(global);
      handlers.get(0).handle(firstFuture);
    }
  
    static <T> void chainHandlers(Future<T> global, List<Handler<Future<T>>> handlers) {
      chainHandlers(res -> {
        if (res.failed()) {
          global.fail(res.cause());
        } else {
          global.complete(res.result());
        }
      }, handlers);
    }
  
    static <T, U> Future<Void> chainOnSuccess(Handler<AsyncResult<T>> globalHandler, Future<U> future, List<Handler<Future<Void>>> list) {
      List<Future<Void>> futures = new ArrayList<>(list.size());
      int i = 0;
      Future<Void> firstFuture = Future.future();
      for (Handler<Future<Void>> handler : list) {
        Future<Void> fut = i == 0 ? firstFuture : futures.get(i - 1);
        futures.add(chainOnSuccess(globalHandler, fut, handler));
        i++;
      }
      future.setHandler(res -> {
        if (res.failed()) {
          globalHandler.handle(Future.failedFuture(res.cause()));
        } else {
          list.get(0).handle(firstFuture);
        }
      });
      return futures.get(futures.size() - 1);
    }
  
    @SafeVarargs // or we're screwed...
    static <T, U> Future<Void> chainOnSuccess(Handler<AsyncResult<T>> globalHandler, Future<U> future, Handler<Future<Void>>... handlers) {
      List<Handler<Future<Void>>> list = Arrays.asList(handlers);
      return AsyncUtils.chainOnSuccess(globalHandler, future, list);
    }
  
    static <T> Handler<AsyncResult<T>> logIfFailed(final String msg, final Logger log) {
      return res -> {
        if (res.failed()) {
          if (msg != null) {
            log.error(msg, res.cause());
          } else {
            log.error(res.cause());
          }
        }
      };
    }
  
  }
  