<commit_before>package thx.promise;

import haxe.ds.Option;
import thx.Error;
import thx.fp.Functions.const;
import thx.Tuple;
import thx.Nil;
using thx.Options;
using thx.Arrays;
import thx.Result;
import thx.Either;

typedef PromiseValue<T> = Result<T, Error>;

@:forward(hasValue, mapAsync, state, then)
abstract Promise<T>(Future<Result<T, Error>>) to Future<Result<T, Error>> {
  inline private function new(future: Future<Result<T, Error>>)
    this = future;

  public static function fromFuture<T>(future : Future<T>) : Promise<T>
    return new Promise(future.map(function(v) return (Right(v) : PromiseValue<T>)));

  public static var nil(default, null) : Promise<Nil> = Promise.value(Nil.nil);

  public static function sequence(arr : Array<Promise<Dynamic>>) : Promise<Nil>
    return Promise.create(function(resolve : Dynamic -> Void, reject) {
      arr = arr.copy();
      function poll() {
        if(arr.isEmpty()) {
          resolve(nil);
        } else {
          arr.shift()
            .success(function(_) poll())
            .failure(reject);
        }
      }
      poll();
    });

  public static function afterAll(arr : Array<Promise<Dynamic>>) : Promise<Nil>
    return Promise.create(function(resolve, reject) {
      all(arr).mapEither(function(_) resolve(Nil.nil), reject);
    });

  public static function all<T>(arr : Array<Promise<T>>) : Promise<Array<T>> {
    if(arr.length == 0)
      return Promise.value([]);
    return Promise.create(function(resolve, reject) {
      var results  = [],
          counter  = 0,
          hasError = false;
      arr.mapi(function(p, i) {
        p.either(function(value) {
          if(hasError) return;
          results[i] = value;
          counter++;
          if(counter == arr.length)
            resolve(results);
        }, function(err) {
          if(hasError) return;
          hasError = true;
          reject(err);
        });
      });
    });
  }

  public static function allSequence<T>(arr : Array<Promise<T>>) : Promise<Array<T>> {
    return Promise.create(function(resolve, reject) {
      var results = [],
          counter = 0;

      function poll() {
        if(counter == arr.length)
          return resolve(results);
        arr[counter++]
          .either(
            function(value) {
              results.push(value);
              poll();
            },
            function(err) {
              reject(err);
            }
          );
      }

      poll();
    });
  }

  public static function create<T>(callback : (T -> Void) -> (Error -> Void) -> Void) : Promise<T>
    return new Promise(
      Future.create(function(cb : PromiseValue<T> -> Void) {
        callback(
          function(value : T) cb((Right(value) : PromiseValue<T>)),
          function(error : Error) cb((Left(error) : PromiseValue<T>))
        );
      })
    );

  public static function createFulfill<T>(callback : (PromiseValue<T> -> Void) -> Void) : Promise<T>
    return new Promise(Future.create(callback));

  public static function fail<T>(message : String, ?pos : haxe.PosInfos) : Promise<T>
    return error(new thx.Error(message, pos));

  public static function error<T>(err : Error) : Promise<T>
    return Promise.create(function(_, reject) reject(err));

  public static function value<T>(v : T) : Promise<T>
    return Promise.create(function(resolve, _) resolve(v));

  public function always(handler : Void -> Void) : Promise<T>
    return new Promise(this.then(function(_) handler()));

  public function either(success : T -> Void, failure : Error -> Void) : Promise<T>
    return new Promise(this.then(function(r) switch r {
      case Right(value): success(value);
      case Left(error): failure(error);
    }));

#if (js || flash || java)
  public function delay(?delayms : Int) : Promise<T>
    return new Promise(this.delay(delayms));
#end

  public function isFailure() : Bool
    return switch this.state {
      case None, Some(Right(_)): false;
      case _: true;
    };

  public function isResolved() : Bool
    return switch this.state {
      case None, Some(Left(_)): false;
      case _: true;
    };

  public function failure(failure : Error -> Void) : Promise<T>
    return either(function(_){}, failure);

  inline public function mapAlways<TOut>(handler : Void -> TOut) : Future<TOut>
    return this.map(function(_) return handler());

  inline public function mapAlwaysAsync<TOut>(handler : (TOut -> Void) -> Void) : Future<TOut>
    return this.mapAsync(function(_, cb) return handler(cb));

  inline public function mapAlwaysFuture<TOut>(handler : Void -> Future<TOut>) : Future<TOut>
    return this.flatMap(function(_) return handler());

  public function mapEither<TOut>(success : T -> TOut, failure : Error -> TOut) : Future<TOut>
    return this.map(function(result)
      return switch result {
        case Right(value): success(value);
        case Left(error):  failure(error);
      });

  public function mapEitherFuture<TOut>(success : T -> Future<TOut>, failure : Error -> Future<TOut>) : Future<TOut>
    return this.flatMap(function(result)
      return switch result {
        case Right(value): success(value);
        case Left(error):  failure(error);
      });

  @:deprecated("Promise.mapFailure is deprecated, use Promise.recover instead")
  public function mapFailure(failure : Error -> T) : Future<T>
    return mapEither(function(value : T) return value, failure);

  @:deprecated("Promise.mapFailureFuture is deprecated, use Promise.recover instead")
  public function mapFailureFuture(failure : Error -> Future<T>) : Future<T>
    return mapEitherFuture(function(value : T) return Future.value(value), failure);

  @:deprecated("Promise.mapFailurePromise is deprecated, use Promise.recover instead")
  public function mapFailurePromise(failure : Error -> Promise<T>) : Promise<T>
    return recover(failure);

  public function recover(failure : Error -> Promise<T>) : Promise<T>
    return new Promise(mapEitherFuture(function(value) return Promise.value(value), failure));

  public function map<U>(success : T -> U) : Promise<U>
    return new Promise(
      mapEitherFuture(
        function(v) return<commit_msg>added recoverAsFuture<commit_after>package thx.promise;

import haxe.ds.Option;
import thx.Error;
import thx.fp.Functions.const;
import thx.Tuple;
import thx.Nil;
using thx.Options;
using thx.Arrays;
import thx.Result;
import thx.Either;

typedef PromiseValue<T> = Result<T, Error>;

@:forward(hasValue, mapAsync, state, then)
abstract Promise<T>(Future<Result<T, Error>>) to Future<Result<T, Error>> {
  inline private function new(future: Future<Result<T, Error>>)
    this = future;

  public static function fromFuture<T>(future : Future<T>) : Promise<T>
    return new Promise(future.map(function(v) return (Right(v) : PromiseValue<T>)));

  public static var nil(default, null) : Promise<Nil> = Promise.value(Nil.nil);

  public static function sequence(arr : Array<Promise<Dynamic>>) : Promise<Nil>
    return Promise.create(function(resolve : Dynamic -> Void, reject) {
      arr = arr.copy();
      function poll() {
        if(arr.isEmpty()) {
          resolve(nil);
        } else {
          arr.shift()
            .success(function(_) poll())
            .failure(reject);
        }
      }
      poll();
    });

  public static function afterAll(arr : Array<Promise<Dynamic>>) : Promise<Nil>
    return Promise.create(function(resolve, reject) {
      all(arr).mapEither(function(_) resolve(Nil.nil), reject);
    });

  public static function all<T>(arr : Array<Promise<T>>) : Promise<Array<T>> {
    if(arr.length == 0)
      return Promise.value([]);
    return Promise.create(function(resolve, reject) {
      var results  = [],
          counter  = 0,
          hasError = false;
      arr.mapi(function(p, i) {
        p.either(function(value) {
          if(hasError) return;
          results[i] = value;
          counter++;
          if(counter == arr.length)
            resolve(results);
        }, function(err) {
          if(hasError) return;
          hasError = true;
          reject(err);
        });
      });
    });
  }

  public static function allSequence<T>(arr : Array<Promise<T>>) : Promise<Array<T>> {
    return Promise.create(function(resolve, reject) {
      var results = [],
          counter = 0;

      function poll() {
        if(counter == arr.length)
          return resolve(results);
        arr[counter++]
          .either(
            function(value) {
              results.push(value);
              poll();
            },
            function(err) {
              reject(err);
            }
          );
      }

      poll();
    });
  }

  public static function create<T>(callback : (T -> Void) -> (Error -> Void) -> Void) : Promise<T>
    return new Promise(
      Future.create(function(cb : PromiseValue<T> -> Void) {
        callback(
          function(value : T) cb((Right(value) : PromiseValue<T>)),
          function(error : Error) cb((Left(error) : PromiseValue<T>))
        );
      })
    );

  public static function createFulfill<T>(callback : (PromiseValue<T> -> Void) -> Void) : Promise<T>
    return new Promise(Future.create(callback));

  public static function fail<T>(message : String, ?pos : haxe.PosInfos) : Promise<T>
    return error(new thx.Error(message, pos));

  public static function error<T>(err : Error) : Promise<T>
    return Promise.create(function(_, reject) reject(err));

  public static function value<T>(v : T) : Promise<T>
    return Promise.create(function(resolve, _) resolve(v));

  public function always(handler : Void -> Void) : Promise<T>
    return new Promise(this.then(function(_) handler()));

  public function either(success : T -> Void, failure : Error -> Void) : Promise<T>
    return new Promise(this.then(function(r) switch r {
      case Right(value): success(value);
      case Left(error): failure(error);
    }));

#if (js || flash || java)
  public function delay(?delayms : Int) : Promise<T>
    return new Promise(this.delay(delayms));
#end

  public function isFailure() : Bool
    return switch this.state {
      case None, Some(Right(_)): false;
      case _: true;
    };

  public function isResolved() : Bool
    return switch this.state {
      case None, Some(Left(_)): false;
      case _: true;
    };

  public function failure(failure : Error -> Void) : Promise<T>
    return either(function(_){}, failure);

  inline public function mapAlways<TOut>(handler : Void -> TOut) : Future<TOut>
    return this.map(function(_) return handler());

  inline public function mapAlwaysAsync<TOut>(handler : (TOut -> Void) -> Void) : Future<TOut>
    return this.mapAsync(function(_, cb) return handler(cb));

  inline public function mapAlwaysFuture<TOut>(handler : Void -> Future<TOut>) : Future<TOut>
    return this.flatMap(function(_) return handler());

  public function mapEither<TOut>(success : T -> TOut, failure : Error -> TOut) : Future<TOut>
    return this.map(function(result)
      return switch result {
        case Right(value): success(value);
        case Left(error):  failure(error);
      });

  public function mapEitherFuture<TOut>(success : T -> Future<TOut>, failure : Error -> Future<TOut>) : Future<TOut>
    return this.flatMap(function(result)
      return switch result {
        case Right(value): success(value);
        case Left(error):  failure(error);
      });

  @:deprecated("Promise.mapFailure is deprecated, use Promise.recoverAsFuture instead")
  public function mapFailure(failure : Error -> T) : Future<T>
    return mapEither(function(value : T) return value, failure);

  @:deprecated("Promise.mapFailureFuture is deprecated, use Promise.recover instead")
  public function mapFailureFuture(failure : Error -> Future<T>) : Future<T>
    return mapEitherFuture(function(value : T) return Future.value(value), failure);

  @:deprecated("Promise.mapFailurePromise is deprecated, use Promise.recover instead")
  public function mapFailurePromise(failure : Error -> Promise<T>) : Promise<T>
    return recover(failure);

  public function recover(failure : Error -> Promise<T>) : Promise<T>
    return new Promise(mapEitherFuture(function(value) return Promise.value(value), failure));

  public function recoverAsFuture(failure : Error -> T) : Future<T>
    return mapEither(function(value : T) return value, failure);

  public function map<U>(success : T -> U) : Promise<U>
    return new Promise(
      mapEitherFuture(
        function(v) return
{'fraction_non_alphanumeric': 0.1387112248161177, 'fraction_numerical': 0.00047969299648225136, 'mean_word_length': 3.1066973079448457, 'pattern_counts': {'":': 0, '<': 144, '<?xml version=': 0, '>': 195, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2458209', 'n_tokens_mistral': 3722, 'n_tokens_neox': 3587, 'n_words': 918}