"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.mapAsyncIterator = mapAsyncIterator;
const fakePromise_js_1 = require("./fakePromise.js");
const map_maybe_promise_js_1 = require("./map-maybe-promise.js");
/**
 * Given an AsyncIterable and a callback function, return an AsyncIterator
 * which produces values mapped via calling the callback function.
 */
function mapAsyncIterator(iterator, onNext, onError, onEnd) {
    if (Symbol.asyncIterator in iterator) {
        iterator = iterator[Symbol.asyncIterator]();
    }
    let $return;
    let abruptClose;
    let onEndWithValue;
    if (onEnd) {
        let onEndWithValueResult /** R in onEndWithValue */;
        onEndWithValue = value => {
            onEndWithValueResult ||= (0, map_maybe_promise_js_1.mapMaybePromise)(onEnd(), () => value);
            return onEndWithValueResult;
        };
    }
    if (typeof iterator.return === 'function') {
        $return = iterator.return;
        abruptClose = (error) => {
            const rethrow = () => {
                throw error;
            };
            return $return.call(iterator).then(rethrow, rethrow);
        };
    }
    function mapResult(result) {
        if (result.done) {
            return onEndWithValue ? onEndWithValue(result) : result;
        }
        return (0, map_maybe_promise_js_1.mapMaybePromise)(result.value, value => (0, map_maybe_promise_js_1.mapMaybePromise)(onNext(value), iteratorResult, abruptClose));
    }
    let mapReject;
    if (onError) {
        let onErrorResult;
        // Capture rejectCallback to ensure it cannot be null.
        const reject = onError;
        mapReject = (error) => {
            onErrorResult ||= (0, map_maybe_promise_js_1.mapMaybePromise)(error, error => (0, map_maybe_promise_js_1.mapMaybePromise)(reject(error), iteratorResult, abruptClose));
            return onErrorResult;
        };
    }
    return {
        next() {
            return iterator.next().then(mapResult, mapReject);
        },
        return() {
            const res$ = $return
                ? $return.call(iterator).then(mapResult, mapReject)
                : (0, fakePromise_js_1.fakePromise)({ value: undefined, done: true });
            return onEndWithValue ? res$.then(onEndWithValue) : res$;
        },
        throw(error) {
            if (typeof iterator.throw === 'function') {
                return iterator.throw(error).then(mapResult, mapReject);
            }
            if (abruptClose) {
                return abruptClose(error);
            }
            return (0, fakePromise_js_1.fakeRejectPromise)(error);
        },
        [Symbol.asyncIterator]() {
            return this;
        },
    };
}
function iteratorResult(value) {
    return { value, done: false };
}
