import {Collection} from './Collection.ts' export type MaybeIterationItem = { done: boolean, value?: T } export type ChunkCallback = (items: Collection) => any export class StopIteration extends Error {} /** * Abstract class representing an iterable, lazy-loaded dataset. * @abstract */ export abstract class Iterable { /** * The current index of the iterable. * @type number */ protected index = 0 /** * Get the item of this iterable at the given index, if one exists. * @param {number} i * @return Promise */ abstract async at_index(i: number): Promise /** * Get the collection of items in the given range of this iterable. * @param {number} start * @param {number} end * @return Promise */ abstract async from_range(start: number, end: number): Promise> /** * Count the number of items in this collection. * @return Promise */ abstract async count(): Promise /** * Get a copy of this iterable. * @return Iterable */ abstract clone(): Iterable /** * Advance to the next value of this iterable. * @return Promise */ public async next(): Promise> { const i = this.index if ( i >= await this.count() ) { return { done: true } } this.index = i + 1 return { done: false, value: await this.at_index(i) } } /** * Function to enable async iteration. * * @example * for await (const item of iterable) {} */ [Symbol.asyncIterator]() { return this } /** * Chunk the iterable into the given size and call the callback passing the chunk along. * @param {number} size * @param {ChunkCallback} callback * @return Promise */ public async chunk(size: number, callback: ChunkCallback) { const total = await this.count() while ( this.index < total ) { const items = await this.from_range(this.index, this.index + size - 1) try { await callback(items) } catch ( error ) { if ( error instanceof StopIteration ) break else throw error } this.index += size } } /** * Advance the iterable to the given index. * @param {number} index * @return Promise */ public async seek(index: number) { if ( index < 0 ) throw new TypeError('Cannot seek to negative index.') else if ( index >= await this.count() ) throw new TypeError('Cannot seek past last item.') this.index = index } /** * Peek at the next value of the iterable, without advancing. * @return Promise */ public async peek(): Promise { if ( this.index + 1 >= await this.count() ) return undefined else return this.at_index(this.index + 1) } /** * Reset the iterable to the first index. * @return Promise */ public async reset() { this.index = 0 } }