2020-07-06 14:53:03 +00:00
|
|
|
import {Collection} from './Collection.ts'
|
|
|
|
|
|
|
|
export type MaybeIterationItem<T> = { done: boolean, value?: T }
|
|
|
|
export type ChunkCallback<T> = (items: Collection<T>) => any
|
|
|
|
|
|
|
|
export class StopIteration extends Error {}
|
|
|
|
|
2020-08-16 19:31:47 +00:00
|
|
|
/**
|
|
|
|
* Abstract class representing an iterable, lazy-loaded dataset.
|
|
|
|
* @abstract
|
|
|
|
*/
|
2020-07-06 14:53:03 +00:00
|
|
|
export abstract class Iterable<T> {
|
2020-08-16 19:31:47 +00:00
|
|
|
/**
|
|
|
|
* The current index of the iterable.
|
|
|
|
* @type number
|
|
|
|
*/
|
2020-07-06 14:53:03 +00:00
|
|
|
protected index = 0
|
|
|
|
|
2020-08-16 19:31:47 +00:00
|
|
|
/**
|
|
|
|
* Get the item of this iterable at the given index, if one exists.
|
|
|
|
* @param {number} i
|
|
|
|
* @return Promise<any|undefined>
|
|
|
|
*/
|
2020-07-19 19:13:48 +00:00
|
|
|
abstract async at_index(i: number): Promise<T | undefined>
|
2020-08-16 19:31:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the collection of items in the given range of this iterable.
|
|
|
|
* @param {number} start
|
|
|
|
* @param {number} end
|
|
|
|
* @return Promise<Collection>
|
|
|
|
*/
|
2020-07-06 14:53:03 +00:00
|
|
|
abstract async from_range(start: number, end: number): Promise<Collection<T>>
|
2020-08-16 19:31:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Count the number of items in this collection.
|
|
|
|
* @return Promise<number>
|
|
|
|
*/
|
2020-07-06 14:53:03 +00:00
|
|
|
abstract async count(): Promise<number>
|
2020-08-16 19:31:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a copy of this iterable.
|
|
|
|
* @return Iterable
|
|
|
|
*/
|
2020-07-06 14:53:03 +00:00
|
|
|
abstract clone(): Iterable<T>
|
|
|
|
|
2020-08-16 19:31:47 +00:00
|
|
|
/**
|
|
|
|
* Advance to the next value of this iterable.
|
|
|
|
* @return Promise<MaybeIterationItem>
|
|
|
|
*/
|
2020-07-06 14:53:03 +00:00
|
|
|
public async next(): Promise<MaybeIterationItem<T>> {
|
|
|
|
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) }
|
|
|
|
}
|
|
|
|
|
2020-08-16 19:31:47 +00:00
|
|
|
/**
|
|
|
|
* Function to enable async iteration.
|
|
|
|
*
|
|
|
|
* @example
|
|
|
|
* for await (const item of iterable) {}
|
|
|
|
*/
|
2020-07-06 14:53:03 +00:00
|
|
|
[Symbol.asyncIterator]() {
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
2020-08-16 19:31:47 +00:00
|
|
|
/**
|
|
|
|
* Chunk the iterable into the given size and call the callback passing the chunk along.
|
|
|
|
* @param {number} size
|
|
|
|
* @param {ChunkCallback} callback
|
|
|
|
* @return Promise<void>
|
|
|
|
*/
|
2020-07-06 14:53:03 +00:00
|
|
|
public async chunk(size: number, callback: ChunkCallback<T>) {
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-16 19:31:47 +00:00
|
|
|
/**
|
|
|
|
* Advance the iterable to the given index.
|
|
|
|
* @param {number} index
|
|
|
|
* @return Promise<void>
|
|
|
|
*/
|
2020-07-06 14:53:03 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-08-16 19:31:47 +00:00
|
|
|
/**
|
|
|
|
* Peek at the next value of the iterable, without advancing.
|
|
|
|
* @return Promise<any|undefined>
|
|
|
|
*/
|
2020-07-06 14:53:03 +00:00
|
|
|
public async peek(): Promise<T | undefined> {
|
|
|
|
if ( this.index + 1 >= await this.count() ) return undefined
|
|
|
|
else return this.at_index(this.index + 1)
|
|
|
|
}
|
|
|
|
|
2020-08-16 19:31:47 +00:00
|
|
|
/**
|
|
|
|
* Reset the iterable to the first index.
|
|
|
|
* @return Promise<any>
|
|
|
|
*/
|
2020-07-06 14:53:03 +00:00
|
|
|
public async reset() {
|
|
|
|
this.index = 0
|
|
|
|
}
|
|
|
|
}
|