import { Future } from '../classes/Future'; import { Result } from '../classes/Result'; export const ITERATOR_EXHAUSTED = Symbol.for('ITERATOR_EXHAUSTED'); type MP = T | PromiseLike; type MR = T | Result; type MPP = MP | MR>; type MRR = MR | MR>; type MPR = MP | MRR>; export type AsyncGenerator = () => MPR; export type AsyncStepper = (previous: I) => MPR; export type AsyncIterator = (input: I) => MPR; export type AsyncValidator = (output: O) => MPR; export function iterateAsync( genfn: AsyncGenerator, iterator: AsyncIterator, validator: AsyncValidator ): Future; export function iterateAsync( iterable: Iterable, iterator: AsyncIterator, validator: AsyncValidator ): Future; export function iterateAsync( init: AsyncGenerator, step: AsyncStepper, iterator: AsyncIterator, validator: AsyncValidator ): Future; export function iterateAsync( ...args: | [AsyncGenerator, AsyncIterator, AsyncValidator] | [Iterable, AsyncIterator, AsyncValidator] | [ AsyncGenerator, AsyncStepper, AsyncIterator, AsyncValidator ] ): Future { if (args.length === 4) { return iterateAsyncResultWrapper(...args); } if (Symbol.iterator in args[0]) { return iterateAsyncIterableWrapper(args[0], args[1], args[2]); } return iterateAsyncResultWrapper(args[0], args[0], args[1], args[2]); } export function iterateAsyncIterableWrapper( iterable: Iterable, iterator: AsyncIterator, validator: AsyncValidator ): Future { const reader = iterable[Symbol.iterator](); const next = () => { const { done, value } = reader.next(); if (done && value === void 0) { throw ITERATOR_EXHAUSTED; } return value; }; return iterateAsyncResultWrapper(next, next, iterator, validator); } export function iterateAsyncResultWrapper( init: AsyncGenerator, step: AsyncStepper, iterator: AsyncIterator, validator: AsyncValidator = () => true ): Future { return new Future((resolve) => { return resolve(iterateAsyncImpl(init, step, iterator, validator)); }); } export async function unwrap(item: MPR): Promise { const transformed = Result.unwrap(await item); if (item === transformed) { return transformed as T; } else { return unwrap(transformed); } } export async function iterateAsyncImpl( init: AsyncGenerator, step: AsyncStepper, iterator: AsyncIterator, validator: AsyncValidator = () => true ): Promise { for ( let input = await unwrap(init()); ; input = await unwrap(step(input)) ) { const output = await unwrap(iterator(input)); const valid = await unwrap(validator(output)); if (valid) { return output; } } } for (const _ of [ iterateAsync, iterateAsyncIterableWrapper, iterateAsyncResultWrapper, iterateAsyncImpl, unwrap, ]) { Object.defineProperties(_, { default: { get: () => iterateAsync }, iterateAsync: { get: () => iterateAsync }, iterateAsyncIterableWrapper: { get: () => iterateAsyncIterableWrapper }, iterateAsyncResultWrapper: { get: () => iterateAsyncResultWrapper }, iterateAsyncImpl: { get: () => iterateAsyncImpl }, unwrap: { get: () => unwrap }, }); }