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 { }
/ * *
* Abstract class representing an iterable , lazy - loaded dataset .
* @abstract
* /
export abstract class Iterable < T > {
/ * *
* 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 < any | undefined >
* /
abstract async at_index ( i : number ) : Promise < T | undefined >
/ * *
* Get the collection of items in the given range of this iterable .
* @param { number } start
* @param { number } end
* @return Promise < Collection >
* /
abstract async from_range ( start : number , end : number ) : Promise < Collection < T > >
/ * *
* Count the number of items in this collection .
* @return Promise < number >
* /
abstract async count ( ) : Promise < number >
/ * *
* Get a copy of this iterable .
* @return Iterable
* /
abstract clone ( ) : Iterable < T >
/ * *
* Advance to the next value of this iterable .
* @return Promise < MaybeIterationItem >
* /
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 ) }
}
/ * *
* 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 < void >
* /
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
}
}
/ * *
* Advance the iterable to the given index .
* @param { number } index
* @return Promise < void >
* /
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 < any | undefined >
* /
public async peek ( ) : Promise < T | undefined > {
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 < any >
* /
public async reset() {
this . index = 0
}
}