@rimbu/stream
Version:
Efficient structure representing a sequence of elements, with powerful operations for TypeScript
2,046 lines (1,716 loc) • 52.4 kB
text/typescript
import { RimbuError, type Token } from '@rimbu/base';
import {
AsyncOptLazy,
Comp,
Eq,
TraverseState,
type ArrayNonEmpty,
type AsyncCollectFun,
type MaybePromise,
type ToJSON,
} from '@rimbu/common';
import { Transformer, type StreamSource } from '@rimbu/stream';
import {
AsyncReducer,
AsyncTransformer,
type AsyncFastIterator,
type AsyncStream,
type AsyncStreamSource,
} from '@rimbu/stream/async';
import {
AsyncAppendIterator,
AsyncCollectIterator,
AsyncConcatIterator,
AsyncDropIterator,
AsyncDropWhileIterator,
AsyncFilterIterator,
AsyncFilterPureIterator,
AsyncIndexedIterator,
AsyncMapIterator,
AsyncMapPureIterator,
AsyncOfIterator,
AsyncPrependIterator,
AsyncReduceIterator,
AsyncRepeatIterator,
AsyncTakeIterator,
AsyncTransformerFastIterator,
AsyncUnfoldIterator,
AsyncZipAllWithItererator,
AsyncZipWithIterator,
FromAsyncIterator,
FromIterator,
FromPromise,
FromResourceIterator,
closeIters,
emptyAsyncFastIterator,
isAsyncFastIterator,
type AsyncStreamConstructors,
} from '@rimbu/stream/async-custom';
import {
StreamConstructorsImpl,
isEmptyStreamSourceInstance,
} from '@rimbu/stream/custom';
export abstract class AsyncStreamBase<T> implements AsyncStream<T> {
abstract [Symbol.asyncIterator](): AsyncFastIterator<T>;
asyncStream(): this {
return this;
}
async equals(
other: AsyncStreamSource<T>,
options: { eq?: Eq<T>; negate?: boolean } = {}
): Promise<boolean> {
const { eq = Eq.objectIs, negate = false } = options;
const it1 = this[Symbol.asyncIterator]();
const it2 = fromAsyncStreamSource(other)[Symbol.asyncIterator]();
const done = Symbol('Done');
while (true) {
const [v1, v2] = await Promise.all([
it1.fastNext(done),
it2.fastNext(done),
] as const);
if (done === v1) {
if (done === v2) return true;
await closeIters(it2);
return false;
}
if (done === v2) {
await closeIters(it1);
return false;
}
if (eq(v1, v2) === negate) {
await closeIters(it1, it2);
return false;
}
}
}
assumeNonEmpty(): AsyncStream.NonEmpty<T> {
return this as unknown as AsyncStream.NonEmpty<T>;
}
asNormal(): AsyncStream<T> {
return this;
}
prepend(value: AsyncOptLazy<T>): AsyncStream.NonEmpty<T> {
return new AsyncPrependStream<T>(this, value).assumeNonEmpty();
}
append(value: AsyncOptLazy<T>): AsyncStream.NonEmpty<T> {
return new AsyncAppendStream<T>(this, value).assumeNonEmpty();
}
async forEach(
f: (value: T, index: number, halt: () => void) => MaybePromise<void>,
options: { state?: TraverseState } = {}
): Promise<void> {
const { state = TraverseState() } = options;
if (state.halted) return;
const done = Symbol('Done');
let value: T | typeof done;
const iterator = this[Symbol.asyncIterator]();
const { halt } = state;
try {
while (
!state.halted &&
done !== (value = await iterator.fastNext(done))
) {
await f(value, state.nextIndex(), halt);
}
} finally {
if (done !== value!) await closeIters(iterator);
}
}
async forEachPure<A extends readonly unknown[]>(
f: (value: T, ...args: A) => MaybePromise<void>,
...args: A
): Promise<void> {
const done = Symbol('Done');
let value: T | typeof done;
const iterator = this[Symbol.asyncIterator]();
try {
while (done !== (value = await iterator.fastNext(done))) {
await f(value, ...args);
}
} finally {
if (done !== value!) {
await closeIters(iterator);
}
}
}
indexed(options: { startIndex?: number } = {}): AsyncStream<[number, T]> {
const { startIndex = 0 } = options;
return new AsyncIndexedStream<T>(this, startIndex);
}
filter(
pred: (value: T, index: number, halt: () => void) => MaybePromise<boolean>,
options: { negate?: boolean | undefined } = {}
): any {
const { negate = false } = options;
return new AsyncFilterStream<T>(this, pred, negate);
}
filterPure<A extends readonly unknown[]>(
options: {
pred: (value: T, ...args: A) => MaybePromise<boolean>;
negate?: boolean | undefined;
},
...args: A
): any {
const { pred, negate = false } = options;
return new AsyncFilterPureStream<T, A>(this, pred, args, negate);
}
withOnly<F extends T>(values: F[]): AsyncStream<F> {
if (values.length <= 0) {
return this as any;
}
const set = new Set<T>(values);
return this.filterPure({ pred: (v) => set.has(v) });
}
without<F extends T>(values: F[]): any {
if (values.length <= 0) {
return this as any;
}
const set = new Set<T>(values);
return this.filterPure({ pred: (v) => set.has(v), negate: true });
}
map<T2>(
mapFun: (value: T, index: number) => MaybePromise<T2>
): AsyncStream<T2> {
return new AsyncMapStream<T, T2>(this, mapFun);
}
mapPure<T2, A extends readonly unknown[]>(
mapFun: (value: T, ...args: A) => MaybePromise<T2>,
...args: A
): AsyncStream<T2> {
return new AsyncMapPureStream<T, A, T2>(this, mapFun, args);
}
collect<R>(collectFun: AsyncCollectFun<T, R>): AsyncStream<R> {
return new AsyncCollectStream<T, R>(this, collectFun);
}
flatMap<T2>(
flatMapFun: (
value: T,
index: number,
halt: () => void
) => AsyncStreamSource<T2>
): AsyncStream<T2> {
return this.transform(AsyncTransformer.flatMap(flatMapFun));
}
flatZip<T2>(
flatMapFun: (
value: T,
index: number,
halt: () => void
) => AsyncStreamSource<T2>
): AsyncStream<[T, T2]> {
return this.transform(AsyncTransformer.flatZip(flatMapFun));
}
transform<R>(transformer: AsyncTransformer.Accept<T, R>): AsyncStream<R> {
return new AsyncTransformerStream(this, transformer);
}
async first<O>(otherwise?: AsyncOptLazy<O>): Promise<T | O> {
const done = Symbol('done');
const iter = this[Symbol.asyncIterator]();
let value: T | typeof done = done;
try {
value = await iter.fastNext(done);
if (done === value) {
return AsyncOptLazy.toPromise(otherwise!);
}
return value;
} finally {
if (done !== value) {
await closeIters(iter);
}
}
}
async last<O>(otherwise?: AsyncOptLazy<O>): Promise<T | O> {
const done = Symbol('Done');
const iterator = this[Symbol.asyncIterator]();
try {
let value: T | typeof done = done;
let lastValue: T | typeof done = done;
while (done !== (value = await iterator.fastNext(done))) {
lastValue = value;
}
if (done === lastValue) {
return AsyncOptLazy.toPromise(otherwise!);
}
return lastValue;
} catch (err) {
await closeIters(iterator);
throw err;
}
}
async single<O>(otherwise?: AsyncOptLazy<O>): Promise<T | O> {
const iterator = this[Symbol.asyncIterator]();
const done = Symbol('Done');
let value: T | typeof done = done;
try {
value = await iterator.fastNext(done);
if (done !== value) {
const resultValue = value;
if (done === (value = await iterator.fastNext(done))) {
return resultValue;
}
}
return AsyncOptLazy.toPromise(otherwise!);
} finally {
if (done !== value) {
await closeIters(iterator);
}
}
}
async count(): Promise<number> {
let result = 0;
const done = Symbol('Done');
const iterator = this[Symbol.asyncIterator]();
try {
while (done !== (await iterator.fastNext(done))) {
result++;
}
return result;
} catch (err) {
await closeIters(iterator);
throw err;
}
}
async countElement(
value: T,
options: { eq?: Eq<T>; negate?: boolean } = {}
): Promise<number> {
const { eq = Eq.objectIs, negate = false } = options;
const done = Symbol('Done');
const iterator = this[Symbol.asyncIterator]();
try {
let result = 0;
let current: T | typeof done;
while (done !== (current = await iterator.fastNext(done))) {
if (eq(value, current) !== negate) {
result++;
}
}
return result;
} catch (err) {
await closeIters(iterator);
throw err;
}
}
async find<O>(
pred: (value: T, index: number) => MaybePromise<boolean>,
options: {
occurrance?: number | undefined;
negate?: boolean | undefined;
otherwise?: AsyncOptLazy<O>;
} = {}
): Promise<T | O> {
const { occurrance = 1, negate = false, otherwise } = options;
if (occurrance <= 0) return AsyncOptLazy.toPromise(otherwise!);
const done = Symbol('Done');
const iterator = this[Symbol.asyncIterator]();
let value: T | typeof done;
let remain = occurrance;
let index = 0;
try {
while (done !== (value = await iterator.fastNext(done))) {
const cond = await pred(value, index++);
if (cond !== negate && --remain <= 0) {
return value;
}
}
return AsyncOptLazy.toPromise(otherwise!);
} finally {
if (done !== value!) {
await closeIters(iterator);
}
}
}
async elementAt<O>(
index: number,
otherwise?: AsyncOptLazy<O>
): Promise<T | O> {
if (index < 0) return AsyncOptLazy.toPromise(otherwise!);
const done = Symbol('Done');
const iterator = this[Symbol.asyncIterator]();
let value: T | typeof done;
let i = 0;
try {
while (i <= index && done !== (value = await iterator.fastNext(done))) {
if (i === index) {
return value;
}
i++;
}
return AsyncOptLazy.toPromise(otherwise!);
} finally {
if (done !== value!) {
await closeIters(iterator);
}
}
}
indicesWhere(
pred: (value: T) => MaybePromise<boolean>,
options: { negate?: boolean } = {}
): AsyncStream<number> {
return this.transform(AsyncTransformer.indicesWhere(pred, options));
}
indicesOf(
searchValue: T,
options: { eq?: Eq<T>; negate?: boolean } = {}
): AsyncStream<number> {
return this.transform(Transformer.indicesOf(searchValue, options));
}
async indexWhere(
pred: (value: T, index: number) => MaybePromise<boolean>,
options: { occurrance?: number; negate?: boolean } = {}
): Promise<number | undefined> {
const { occurrance = 1, negate = false } = options;
if (occurrance <= 0) {
return undefined;
}
const done = Symbol('Done');
let value: T | typeof done;
const iterator = this[Symbol.asyncIterator]();
let index = 0;
let occ = 0;
try {
while (done !== (value = await iterator.fastNext(done))) {
const i = index++;
const cond = await pred(value, i);
if (cond === !negate) {
occ++;
if (occ >= occurrance) {
return i;
}
}
}
return undefined;
} finally {
if (done !== value!) {
await closeIters(iterator);
}
}
}
async indexOf(
searchValue: T,
options: {
occurrance?: number | undefined;
eq?: Eq<T> | undefined;
negate?: boolean | undefined;
} = {}
): Promise<number | undefined> {
const { occurrance = 1, eq = Eq.objectIs, negate = false } = options;
if (occurrance <= 0) return undefined;
const done = Symbol('Done');
let value: T | typeof done;
const iterator = this[Symbol.asyncIterator]();
let index = 0;
let occ = 0;
try {
while (done !== (value = await iterator.fastNext(done))) {
const i = index++;
if (eq(value, searchValue) !== negate) {
occ++;
if (occ >= occurrance) {
return i;
}
}
}
return undefined;
} finally {
if (done !== value!) {
await closeIters(iterator);
}
}
}
async some(
pred: (value: T, index: number) => MaybePromise<boolean>,
options: { negate?: boolean } = {}
): Promise<boolean> {
return undefined !== (await this.indexWhere(pred, options));
}
async every(
pred: (value: T, index: number) => MaybePromise<boolean>,
options: { negate?: boolean } = {}
): Promise<boolean> {
const { negate = false } = options;
return undefined === (await this.indexWhere(pred, { negate: !negate }));
}
async contains(
searchValue: T,
options: { amount?: number; eq?: Eq<T>; negate?: boolean } = {}
): Promise<boolean> {
const { amount = 1 } = options;
if (amount <= 0) {
return true;
}
const { eq = Eq.objectIs, negate = false } = options;
return (
undefined !==
(await this.indexOf(searchValue, { occurrance: amount, eq, negate }))
);
}
async containsSlice(
source: AsyncStreamSource.NonEmpty<T>,
options: { eq?: Eq<T>; amount?: number } = {}
): Promise<boolean> {
return (this as AsyncStream<T>).reduce(
AsyncReducer.containsSlice(source, options)
);
}
takeWhile(
pred: (value: T, index: number) => MaybePromise<boolean>,
options: { negate?: boolean } = {}
): AsyncStream<T> {
const { negate = false } = options;
return this.filter(async (value, index, halt) => {
const result = (await pred(value, index)) !== negate;
if (!result) {
halt();
}
return result;
});
}
dropWhile(
pred: (value: T, index: number) => MaybePromise<boolean>,
options: { negate?: boolean } = {}
): AsyncStream<T> {
const { negate = false } = options;
return new AsyncDropWhileStream(this, pred, negate);
}
take(amount: number): AsyncStream<T> {
if (amount <= 0) {
return emptyAsyncStream;
}
return new AsyncTakeStream<T>(this, amount);
}
drop(amount: number): AsyncStream<T> {
if (amount <= 0) {
return this;
}
return new AsyncDropStream<T>(this, amount);
}
repeat(amount?: number): AsyncStream<T> {
if (undefined !== amount && amount <= 1) {
return this;
}
return new AsyncFromStream<T>(
() => new AsyncRepeatIterator<T>(this, amount)
);
}
concat(...others: ArrayNonEmpty<AsyncStreamSource<T>>): any {
if (others.every(isEmptyAsyncStreamSourceInstance)) return this;
return new AsyncConcatStream<T>(this, others);
}
min<O>(otherwise?: AsyncOptLazy<O>): Promise<T | O> {
return this.minBy(Comp.defaultComp().compare, otherwise);
}
async minBy<O>(
compare: (v1: T, v2: T) => number,
otherwise?: AsyncOptLazy<O>
): Promise<T | O> {
const done = Symbol('Done');
const iterator = this[Symbol.asyncIterator]();
let result: T | typeof done;
let value: T | typeof done;
try {
result = await iterator.fastNext(done);
if (done === result) return AsyncOptLazy.toPromise(otherwise!);
while (done !== (value = await iterator.fastNext(done))) {
if (compare(value, result) < 0) result = value;
}
return result;
} catch (err) {
await closeIters(iterator);
throw err;
}
}
max<O>(otherwise?: AsyncOptLazy<O>): Promise<T | O> {
return this.maxBy(Comp.defaultComp().compare, otherwise);
}
async maxBy<O>(
compare: (v1: T, v2: T) => number,
otherwise?: AsyncOptLazy<O>
): Promise<T | O> {
const done = Symbol('Done');
const iterator = this[Symbol.asyncIterator]();
try {
let result: T | typeof done = await iterator.fastNext(done);
if (done === result) {
return AsyncOptLazy.toPromise(otherwise!);
}
let value: T | typeof done;
while (done !== (value = await iterator.fastNext(done))) {
if (compare(value, result) > 0) result = value;
}
return result;
} catch (err) {
await closeIters(iterator);
throw err;
}
}
intersperse(sep: AsyncStreamSource<T>): AsyncStream<T> {
if (isEmptyAsyncStreamSourceInstance(sep)) {
return this;
}
return this.transform(AsyncTransformer.intersperse(sep));
}
async join({
sep = '',
start = '',
end = '',
valueToString = String,
ifEmpty = undefined,
} = {}): Promise<string> {
const done = Symbol('Done');
const iterator = this[Symbol.asyncIterator]();
let value: T | typeof done = await iterator.fastNext(done);
try {
if (done === value) {
if (undefined !== ifEmpty) return ifEmpty;
return start.concat(end);
}
let result = start.concat(valueToString(value));
while (done !== (value = await iterator.fastNext(done))) {
result = result.concat(sep, valueToString(value));
}
return result.concat(end);
} catch (err) {
await closeIters(iterator);
throw err;
}
}
mkGroup({
sep = emptyAsyncStream as AsyncStreamSource<T>,
start = emptyAsyncStream as AsyncStreamSource<T>,
end = emptyAsyncStream as AsyncStreamSource<T>,
} = {}): any {
return fromAsyncStreamSource(start).concat(this.intersperse(sep), end);
}
splitWhere<R>(
pred: (value: T, index: number) => MaybePromise<boolean>,
options: {
negate?: boolean | undefined;
collector?: AsyncReducer.Accept<T, R> | undefined;
} = {}
): AsyncStream<R> {
return this.transform(AsyncTransformer.splitWhere(pred, options));
}
splitOn<R>(
sepElem: T,
options: {
eq?: Eq<T> | undefined;
negate?: boolean | undefined;
collector?: AsyncReducer.Accept<T, R> | undefined;
} = {}
): AsyncStream<R> {
return this.transform(AsyncTransformer.splitOn(sepElem, options));
}
splitOnSlice<R>(
sepSlice: AsyncStreamSource<T>,
options: {
eq?: Eq<T> | undefined;
collector?: AsyncReducer.Accept<T, R> | undefined;
} = {}
): AsyncStream<R> {
return this.transform(AsyncTransformer.splitOnSlice(sepSlice, options));
}
distinctPrevious(
options: { eq?: Eq<T> | undefined; negate?: boolean | undefined } = {}
): AsyncStream<T> {
return this.transform(Transformer.distinctPrevious(options));
}
window<R>(
windowSize: number,
options: {
skipAmount?: number | undefined;
collector?: AsyncReducer.Accept<T, R> | undefined;
} = {}
): AsyncStream<R> {
return this.transform(AsyncTransformer.window(windowSize, options as any));
}
partition(
pred: (value: T, index: number) => MaybePromise<boolean>,
options: {
collectorTrue?: any;
collectorFalse?: any;
} = {}
): Promise<[any, any]> {
return (this as AsyncStream<T>).reduce<[any, any]>(
AsyncReducer.partition(pred, options)
);
}
groupBy<K, R>(
valueToKey: (value: T, index: number) => MaybePromise<K>,
options: {
collector?: AsyncReducer.Accept<readonly [K, T], R> | undefined;
} = {}
): Promise<R> {
return (this as AsyncStream<T>).reduce(
AsyncReducer.groupBy<T, K, R>(valueToKey, options as any)
);
}
async fold<R>(
init: AsyncOptLazy<R>,
next: (
current: R,
value: T,
index: number,
halt: () => void
) => MaybePromise<R>
): Promise<R> {
return (this as AsyncStream<T>).reduce(AsyncReducer.fold(init, next));
}
foldStream<R>(
init: AsyncOptLazy<R>,
next: (
current: R,
value: T,
index: number,
halt: () => void
) => MaybePromise<R>
): AsyncStream<R> {
return (this as AsyncStream<T>).reduceStream(AsyncReducer.fold(init, next));
}
async reduce<const S extends AsyncReducer.CombineShape<T>>(
shape: S & AsyncReducer.CombineShape<T>
): Promise<AsyncReducer.CombineResult<S>> {
const reducerInstance = (await AsyncReducer.combine<T, S>(
shape
).compile()) as AsyncReducer.Instance<T, AsyncReducer.CombineResult<S>>;
const done = Symbol('Done');
let value: T | typeof done = done;
const iter = this[Symbol.asyncIterator]();
try {
while (
!reducerInstance.halted &&
done !== (value = await iter.fastNext(done))
) {
await reducerInstance.next(value);
}
return await reducerInstance.getOutput();
} finally {
await closeIters(iter);
await reducerInstance.onClose();
}
}
reduceStream<const S extends AsyncReducer.CombineShape<T>>(
shape: S & AsyncReducer.CombineShape<T>
): AsyncStream<AsyncReducer.CombineResult<S>> {
const reducer = AsyncReducer.combine<T, S>(shape) as AsyncReducer<
T,
AsyncReducer.CombineResult<S>
>;
return new AsyncReduceStream(this, reducer);
}
async toArray(): Promise<T[]> {
const iterator = this[Symbol.asyncIterator]();
const result: T[] = [];
const done = Symbol('Done');
let value: T | typeof done;
try {
while (done !== (value = await iterator.fastNext(done))) {
result.push(value);
}
return result;
} catch (err) {
await closeIters(iterator);
throw err;
}
}
toString(): string {
return `AsyncStream(...<potentially empty>)`;
}
async toJSON(): Promise<ToJSON<T[], 'AsyncStream'>> {
return {
dataType: 'AsyncStream',
value: await this.toArray(),
};
}
}
export class AsyncFromStream<T> extends AsyncStreamBase<T> {
[Symbol.asyncIterator]: () => AsyncFastIterator<T> = undefined as any;
constructor(createIterator: () => AsyncFastIterator<T>) {
super();
this[Symbol.asyncIterator] = createIterator;
}
}
class AsyncPrependStream<T> extends AsyncStreamBase<T> {
constructor(
readonly source: AsyncStream<T>,
readonly item: AsyncOptLazy<T>
) {
super();
}
[Symbol.asyncIterator](): AsyncFastIterator<T> {
return new AsyncPrependIterator<T>(
this.source[Symbol.asyncIterator](),
this.item
);
}
async first(): Promise<T> {
return AsyncOptLazy.toPromise(this.item);
}
async last(): Promise<T> {
return this.source.last(this.item);
}
async count(): Promise<number> {
return (await this.source.count()) + 1;
}
async forEach(
f: (value: T, index: number, halt: () => void) => MaybePromise<void>,
options: { state?: TraverseState } = {}
): Promise<void> {
const { state = TraverseState() } = options;
if (state.halted) return;
await f(
await AsyncOptLazy.toMaybePromise(this.item),
state.nextIndex(),
state.halt
);
if (state.halted) return;
await this.source.forEach(f, { state });
}
mapPure<T2, A extends readonly unknown[]>(
mapFun: (value: T, ...args: A) => MaybePromise<T2>,
...args: A
): AsyncStream<T2> {
return new AsyncPrependStream(
this.source.mapPure(mapFun, ...args),
async () => mapFun(await AsyncOptLazy.toMaybePromise(this.item), ...args)
);
}
take(amount: number): AsyncStream<T> {
if (amount <= 0) {
return emptyAsyncStream;
}
if (amount === 1) {
return AsyncStreamConstructorsImpl.of(this.item);
}
return new AsyncPrependStream(this.source.take(amount - 1), this.item);
}
drop(amount: number): AsyncStream<T> {
if (amount <= 0) {
return this;
}
if (amount === 1) {
return this.source;
}
return this.source.drop(amount - 1);
}
async minBy<O>(compare: (v1: T, v2: T) => number): Promise<T | O> {
const token = Symbol();
const result = await this.source.minBy(compare, token);
const itemValue = await AsyncOptLazy.toMaybePromise(this.item);
if (token === result) {
return itemValue;
}
return compare(result, itemValue) <= 0 ? result : itemValue;
}
async maxBy<O>(compare: (v1: T, v2: T) => number): Promise<T | O> {
const token = Symbol();
const result = await this.source.maxBy(compare, token);
const itemValue = await AsyncOptLazy.toMaybePromise(this.item);
if (token === result) {
return itemValue;
}
return compare(result, itemValue) > 0 ? result : itemValue;
}
async toArray(): Promise<T[]> {
const result = await this.source.toArray();
result.unshift(await AsyncOptLazy.toMaybePromise(this.item));
return result;
}
}
class AsyncAppendStream<T> extends AsyncStreamBase<T> {
constructor(
readonly source: AsyncStream<T>,
readonly item: AsyncOptLazy<T>
) {
super();
}
[Symbol.asyncIterator](): AsyncFastIterator<T> {
return new AsyncAppendIterator<T>(
this.source[Symbol.asyncIterator](),
this.item
);
}
first(): Promise<T> {
return this.source.first(this.item);
}
last(): Promise<T> {
return AsyncOptLazy.toPromise(this.item);
}
async count(): Promise<number> {
return (await this.source.count()) + 1;
}
async forEach(
f: (value: T, index: number, halt: () => void) => MaybePromise<void>,
options: { state?: TraverseState } = {}
): Promise<void> {
const { state = TraverseState() } = options;
if (state.halted) return;
await this.source.forEach(f, { state });
if (state.halted) return;
await f(
await AsyncOptLazy.toMaybePromise(this.item),
state.nextIndex(),
state.halt
);
}
mapPure<T2, A extends readonly unknown[]>(
mapFun: (value: T, ...args: A) => MaybePromise<T2>,
...args: A
): AsyncStream<T2> {
return new AsyncAppendStream(
this.source.mapPure(mapFun, ...args),
async () => mapFun(await AsyncOptLazy.toMaybePromise(this.item), ...args)
);
}
async minBy<O>(compare: (v1: T, v2: T) => number): Promise<T | O> {
const token = Symbol();
const result = await this.source.minBy(compare, token);
const itemValue = await AsyncOptLazy.toMaybePromise(this.item);
if (token === result) {
return itemValue;
}
return compare(result, itemValue) <= 0 ? result : itemValue;
}
async maxBy<O>(compare: (v1: T, v2: T) => number): Promise<T | O> {
const token = Symbol();
const result = await this.source.maxBy(compare, token);
const itemValue = await AsyncOptLazy.toMaybePromise(this.item);
if (token === result) {
return itemValue;
}
return compare(result, itemValue) > 0 ? result : itemValue;
}
async toArray(): Promise<T[]> {
const result = await this.source.toArray();
result.push(await AsyncOptLazy.toMaybePromise(this.item));
return result;
}
}
class AsyncIndexedStream<T> extends AsyncStreamBase<[number, T]> {
constructor(
readonly source: AsyncStream<T>,
readonly startIndex: number
) {
super();
}
[Symbol.asyncIterator](): AsyncFastIterator<[number, T]> {
return new AsyncIndexedIterator<T>(
this.source[Symbol.asyncIterator](),
this.startIndex
);
}
count(): Promise<number> {
return this.source.count();
}
}
class AsyncMapStream<T, T2> extends AsyncStreamBase<T2> {
constructor(
readonly source: AsyncStream<T>,
readonly mapFun: (value: T, index: number) => MaybePromise<T2>
) {
super();
}
[Symbol.asyncIterator](): AsyncFastIterator<T2> {
return new AsyncMapIterator(
this.source[Symbol.asyncIterator](),
this.mapFun
);
}
async first<O>(otherwise?: AsyncOptLazy<O>): Promise<T2 | O> {
const done = Symbol('Done');
const value = await this.source.first(done);
if (done === value) return AsyncOptLazy.toPromise(otherwise!);
return this.mapFun(value, 0);
}
async last<O>(otherwise?: AsyncOptLazy<O>): Promise<T2 | O> {
const done = Symbol('Done');
const value = await this.source.last(done);
if (done === value) return AsyncOptLazy.toPromise(otherwise!);
return this.mapFun(value, 0);
}
async count(): Promise<number> {
return this.source.count();
}
async elementAt<O>(
index: number,
otherwise?: AsyncOptLazy<O>
): Promise<T2 | O> {
const done = Symbol('Done');
const value = await this.source.elementAt(index, done);
if (done === value) return AsyncOptLazy.toPromise(otherwise!);
return this.mapFun(value, index);
}
map<T3>(
mapFun: (value: T2, index: number) => MaybePromise<T3>
): AsyncStream<T3> {
return new AsyncMapStream<T, T3>(this.source, async (value, index) =>
mapFun(await this.mapFun(value, index), index)
);
}
take(amount: number): AsyncStream<T2> {
if (amount <= 0) {
return emptyAsyncStream;
}
return new AsyncMapStream(this.source.take(amount), this.mapFun);
}
drop(amount: number): AsyncStream<T2> {
if (amount <= 0) {
return this;
}
return new AsyncMapStream(this.source.drop(amount), this.mapFun);
}
}
class AsyncMapPureStream<
T,
A extends readonly unknown[],
T2,
> extends AsyncStreamBase<T2> {
constructor(
readonly source: AsyncStream<T>,
readonly mapFun: (value: T, ...args: A) => MaybePromise<T2>,
readonly args: A
) {
super();
}
[Symbol.asyncIterator](): AsyncFastIterator<T2> {
return new AsyncMapPureIterator<T, A, T2>(
this.source[Symbol.asyncIterator](),
this.mapFun,
this.args
);
}
async first<O>(otherwise?: AsyncOptLazy<O>): Promise<T2 | O> {
const done = Symbol('Done');
const value = await this.source.first(done);
if (done === value) return AsyncOptLazy.toPromise(otherwise!);
return this.mapFun(value, ...this.args);
}
async last<O>(otherwise?: AsyncOptLazy<O>): Promise<T2 | O> {
const done = Symbol('Done');
const value = await this.source.last(done);
if (done === value) return AsyncOptLazy.toPromise(otherwise!);
return this.mapFun(value, ...this.args);
}
async count(): Promise<number> {
return this.source.count();
}
async elementAt<O>(
index: number,
otherwise?: AsyncOptLazy<O>
): Promise<T2 | O> {
const done = Symbol('Done');
const value = await this.source.elementAt(index, done);
if (done === value) return AsyncOptLazy.toPromise(otherwise!);
return this.mapFun(value, ...this.args);
}
mapPure<T3, A2 extends readonly unknown[]>(
mapFun: (value: T2, ...args: A2) => MaybePromise<T3>,
...args: A2
): AsyncStream<T3> {
return new AsyncMapPureStream<T, A2, T3>(
this.source,
async (value, ...args) =>
mapFun(await this.mapFun(value, ...this.args), ...args),
args
);
}
take(amount: number): AsyncStream<T2> {
if (amount <= 0) {
return emptyAsyncStream;
}
return new AsyncMapPureStream(
this.source.take(amount),
this.mapFun,
this.args
);
}
drop(amount: number): AsyncStream<T2> {
if (amount <= 0) {
return this;
}
return new AsyncMapPureStream(
this.source.drop(amount),
this.mapFun,
this.args
);
}
}
class AsyncConcatStream<T> extends AsyncStreamBase<T> {
constructor(
readonly source: AsyncStream<T>,
readonly otherSources: AsyncStreamSource<T>[]
) {
super();
}
[Symbol.asyncIterator](): AsyncFastIterator<T> {
return new AsyncConcatIterator(
this.source,
this.otherSources,
asyncStreamSourceHelpers
);
}
async forEach(
f: (value: T, index: number, halt: () => void) => MaybePromise<void>,
options: { state?: TraverseState } = {}
): Promise<void> {
const { state = TraverseState() } = options;
if (state.halted) return;
await this.source.forEach(f, { state });
let sourceIndex = -1;
const sources = this.otherSources;
const length = sources.length;
while (!state.halted && ++sourceIndex < length) {
const source = sources[sourceIndex];
if (!isEmptyAsyncStreamSourceInstance(source)) {
await fromAsyncStreamSource(source).forEach(f, { state });
}
}
}
async forEachPure<A extends readonly unknown[]>(
f: (value: T, ...args: A) => MaybePromise<void>,
...args: A
): Promise<void> {
await this.source.forEachPure(f, ...args);
let sourceIndex = -1;
const sources = this.otherSources;
const length = sources.length;
while (++sourceIndex < length) {
const source = sources[sourceIndex];
if (!isEmptyAsyncStreamSourceInstance(source)) {
await fromAsyncStreamSource(source).forEachPure(f, ...args);
}
}
}
async last<O>(otherwise?: AsyncOptLazy<O>): Promise<T | O> {
const sources = this.otherSources;
let sourceIndex = sources.length;
while (--sourceIndex >= 0) {
const source = sources[sourceIndex];
if (!isEmptyAsyncStreamSourceInstance(source)) {
const done = Symbol('Done');
const value = await fromAsyncStreamSource(source).last(done);
if (done !== value) return value;
}
}
return this.source.last(otherwise!);
}
async count(): Promise<number> {
let result = await this.source.count();
const sources = this.otherSources;
const length = sources.length;
let sourceIndex = -1;
while (++sourceIndex < length) {
const source = sources[sourceIndex];
if (!isEmptyAsyncStreamSourceInstance(source)) {
result += await fromAsyncStreamSource(source).count();
}
}
return result;
}
filterPure<A extends readonly unknown[]>(
options: {
pred: (value: T, ...args: A) => MaybePromise<boolean>;
negate?: boolean | undefined;
},
...args: A
): any {
return new AsyncConcatStream(
this.source.filterPure(options, ...args),
this.otherSources.map((source) =>
fromAsyncStreamSource(source).filterPure(options, ...args)
)
);
}
mapPure<T2, A extends readonly unknown[]>(
mapFun: (value: T, ...args: A) => MaybePromise<T2>,
...args: A
): AsyncStream<T2> {
return new AsyncConcatStream(
this.source.mapPure(mapFun, ...args),
this.otherSources.map((source) =>
fromAsyncStreamSource(source).mapPure(mapFun, ...args)
)
);
}
concat<T2>(...others2: AsyncStreamSource<T2>[]): any {
return new AsyncConcatStream<T | T2>(
this.source,
(this.otherSources as AsyncStreamSource<T | T2>[]).concat(others2)
);
}
async toArray(): Promise<T[]> {
let result: T[] = await this.source.toArray();
let sourceIndex = -1;
const sources = this.otherSources;
const length = sources.length;
while (++sourceIndex < length) {
const source = sources[sourceIndex];
if (!isEmptyAsyncStreamSourceInstance(source)) {
result = result.concat(await fromAsyncStreamSource(source).toArray());
}
}
return result;
}
}
class AsyncFilterStream<T> extends AsyncStreamBase<T> {
constructor(
readonly source: AsyncStream<T>,
readonly pred: (
value: T,
index: number,
halt: () => void
) => MaybePromise<boolean>,
readonly negate = false
) {
super();
}
[Symbol.asyncIterator](): AsyncFastIterator<T> {
return new AsyncFilterIterator<T>(
this.source[Symbol.asyncIterator](),
this.pred,
this.negate
);
}
filterPure<A extends readonly unknown[]>(
options: {
pred: (value: T, ...args: A) => MaybePromise<boolean>;
negate?: boolean | undefined;
},
...args: A
): any {
const { pred, negate = false } = options;
const { pred: thisPred, negate: thisNegate } = this;
return new AsyncFilterStream(
this.source,
async (value, index, halt) =>
(await thisPred(value, index, halt)) !== thisNegate &&
(await pred(value, ...args)) !== negate
);
}
mapPure<T2, A extends readonly unknown[]>(
mapFun: (value: T, ...args: A) => MaybePromise<T2>,
...args: A
): AsyncStream<T2> {
const { pred, negate } = this;
return new AsyncCollectStream(
this.source,
async (value, index, skip, halt) =>
(await pred(value, index, halt)) !== negate
? mapFun(value, ...args)
: skip
);
}
}
class AsyncFilterPureStream<
T,
A extends readonly unknown[],
> extends AsyncStreamBase<T> {
constructor(
readonly source: AsyncStream<T>,
readonly pred: (value: T, ...args: A) => MaybePromise<boolean>,
readonly args: A,
readonly negate = false
) {
super();
}
[Symbol.asyncIterator](): AsyncFastIterator<T> {
return new AsyncFilterPureIterator<T, A>(
this.source[Symbol.asyncIterator](),
this.pred,
this.args,
this.negate
);
}
filterPure<A extends readonly unknown[]>(
options: {
pred: (value: T, ...args: A) => MaybePromise<boolean>;
negate?: boolean | undefined;
},
...args: A
): any {
const { pred, negate = false } = options;
const thisPred = this.pred;
const thisArgs = this.args;
const thisNegate = this.negate;
return new AsyncFilterPureStream(
this.source,
async (value, ...args) => {
return (
(await thisPred(value, ...thisArgs)) !== thisNegate &&
(await pred(value, ...args)) !== negate
);
},
args
);
}
mapPure<T2, A extends readonly unknown[]>(
mapFun: (value: T, ...args: A) => MaybePromise<T2>,
...args: A
): AsyncStream<T2> {
const { pred, negate, args: thisArgs } = this;
return new AsyncCollectStream(this.source, async (value, _, skip) =>
(await pred(value, ...thisArgs)) !== negate
? mapFun(value, ...args)
: skip
);
}
}
class AsyncCollectStream<T, R> extends AsyncStreamBase<R> {
constructor(
readonly source: AsyncStream<T>,
readonly collectFun: AsyncCollectFun<T, R>
) {
super();
}
[Symbol.asyncIterator](): AsyncFastIterator<R> {
return new AsyncCollectIterator<T, R>(
this.source[Symbol.asyncIterator](),
this.collectFun
);
}
filterPure<A extends readonly unknown[]>(
options: {
pred: (value: R, ...args: A) => MaybePromise<boolean>;
negate?: boolean | undefined;
},
...args: A
): any {
const { pred, negate = false } = options;
const { collectFun } = this;
return new AsyncCollectStream(
this.source,
async (value, index, skip, halt) => {
const result = await collectFun(value, index, skip, halt);
if (skip === result || (await pred(result, ...args)) === negate) {
return skip;
}
return result;
}
);
}
mapPure<T2, A extends readonly unknown[]>(
mapFun: (value: R, ...args: A) => MaybePromise<T2>,
...args: A
): AsyncStream<T2> {
const { collectFun } = this;
return new AsyncCollectStream(
this.source,
async (value, index, skip, halt) => {
const result = await collectFun(value, index, skip, halt);
if (skip === result) {
return skip;
}
return mapFun(result, ...args);
}
);
}
}
class AsyncDropWhileStream<T> extends AsyncStreamBase<T> {
constructor(
readonly source: AsyncStream<T>,
readonly pred: (value: T, index: number) => MaybePromise<boolean>,
readonly negate: boolean
) {
super();
}
[Symbol.asyncIterator](): AsyncFastIterator<T> {
return new AsyncDropWhileIterator<T>(
this.source[Symbol.asyncIterator](),
this.pred,
this.negate
);
}
}
class AsyncTakeStream<T> extends AsyncStreamBase<T> {
constructor(
readonly source: AsyncStream<T>,
readonly amount: number
) {
super();
}
[Symbol.asyncIterator](): AsyncFastIterator<T> {
return new AsyncTakeIterator<T>(
this.source[Symbol.asyncIterator](),
this.amount
);
}
take(amount: number): AsyncStream<T> {
if (amount <= 0) {
return emptyAsyncStream;
}
if (amount >= this.amount) {
return this;
}
return this.source.take(amount);
}
}
class AsyncDropStream<T> extends AsyncStreamBase<T> {
constructor(
readonly source: AsyncStream<T>,
readonly amount: number
) {
super();
}
[Symbol.asyncIterator](): AsyncFastIterator<T> {
return new AsyncDropIterator<T>(
this.source[Symbol.asyncIterator](),
this.amount
);
}
drop(amount: number): AsyncStream<T> {
if (amount <= 0) {
return this;
}
return this.source.drop(this.amount + amount);
}
}
class AsyncReduceStream<I, R = I> extends AsyncStreamBase<R> {
constructor(
readonly source: AsyncStream<I>,
readonly reducer: AsyncReducer<I, R>
) {
super();
}
[Symbol.asyncIterator](): AsyncFastIterator<R> {
return new AsyncReduceIterator<I, R>(
this.source[Symbol.asyncIterator](),
this.reducer
);
}
}
export class AsyncTransformerStream<T, R = T> extends AsyncStreamBase<R> {
constructor(
readonly source: AsyncStream<T>,
readonly transformer: AsyncTransformer.Accept<T, R>
) {
super();
}
[Symbol.asyncIterator](): AsyncFastIterator<R> {
return new AsyncTransformerFastIterator<T, R>(
this.source[Symbol.asyncIterator](),
this.transformer
);
}
}
export class AsyncOfStream<T> extends AsyncStreamBase<T> {
constructor(readonly values: ArrayNonEmpty<AsyncOptLazy<T>>) {
super();
}
[Symbol.asyncIterator](): AsyncFastIterator<T> {
return new AsyncOfIterator<T>(this.values);
}
}
export function isAsyncStream(obj: any): obj is AsyncStream<any> {
return obj instanceof AsyncStreamBase;
}
class AsyncEmptyStream<T = any>
extends AsyncStreamBase<T>
implements AsyncStream<T>
{
[Symbol.asyncIterator](): AsyncFastIterator<T> {
return emptyAsyncFastIterator;
}
asyncStream(): this {
return this;
}
async equals(other: AsyncStreamSource<T>): Promise<boolean> {
if (other === this) return true;
const done = Symbol('done');
return (
done ===
fromAsyncStreamSource(other)[Symbol.asyncIterator]().fastNext(done)
);
}
prepend(value: AsyncOptLazy<T>): AsyncStream.NonEmpty<T> {
return AsyncStreamConstructorsImpl.of(value);
}
append(value: AsyncOptLazy<T>): AsyncStream.NonEmpty<T> {
return AsyncStreamConstructorsImpl.of(value);
}
assumeNonEmpty(): never {
RimbuError.throwEmptyCollectionAssumedNonEmptyError();
}
async forEach(): Promise<void> {
//
}
async forEachPure(): Promise<void> {
//
}
indexed(): AsyncStream<[number, T]> {
return this as any;
}
map<T2>(): AsyncStream<T2> {
return this as any;
}
mapPure<T2>(): AsyncStream<T2> {
return this as any;
}
flatMap<T2>(): AsyncStream<T2> {
return this as any;
}
flatZip<T2>(): AsyncStream<[T, T2]> {
return this as any;
}
transform<R>(transformer: AsyncTransformer<T, R>): AsyncStream<R> {
return fromAsyncStreamSource<R>(async () => {
const instance = await transformer.compile();
return instance.getOutput();
});
}
filter(): any {
return this;
}
filterPure(): any {
return this;
}
collect<R>(): AsyncStream<R> {
return this as any;
}
first<O>(otherwise?: AsyncOptLazy<O>): Promise<O> {
return AsyncOptLazy.toPromise(otherwise!);
}
last<O>(otherwise?: AsyncOptLazy<O>): Promise<O> {
return AsyncOptLazy.toPromise(otherwise!);
}
single<O>(otherwise?: AsyncOptLazy<O>): Promise<T | O> {
return AsyncOptLazy.toPromise(otherwise!);
}
async count(): Promise<0> {
return 0;
}
async countElement(): Promise<0> {
return 0;
}
async countNotElement(): Promise<0> {
return 0;
}
find<O>(
pred: (value: any, index: number) => boolean,
options: { otherwise?: AsyncOptLazy<O> } = {}
): Promise<O> {
const { otherwise } = options;
return AsyncOptLazy.toPromise(otherwise!);
}
elementAt<O>(index: number, otherwise?: AsyncOptLazy<O>): Promise<O> {
return AsyncOptLazy.toPromise(otherwise!);
}
indicesWhere(): AsyncStream<number> {
return this as any;
}
indicesOf(): AsyncStream<number> {
return this as any;
}
async indexWhere(): Promise<undefined> {
return undefined;
}
async indexOf(): Promise<undefined> {
return undefined;
}
async some(): Promise<false> {
return false;
}
async every(): Promise<true> {
return true;
}
async contains(): Promise<false> {
return false;
}
async containsSlice(): Promise<false> {
return false;
}
takeWhile(): AsyncStream<T> {
return this;
}
dropWhile(): AsyncStream<T> {
return this;
}
take(): AsyncStream<T> {
return this;
}
drop(): AsyncStream<T> {
return this;
}
repeat(): AsyncStream<T> {
return this;
}
concat(...others: ArrayNonEmpty<AsyncStreamSource<T>>): any {
if (others.every(isEmptyAsyncStreamSourceInstance)) return this;
const [source1, source2, ...sources] = others;
if (undefined === source2) return source1;
return fromAsyncStreamSource(source1).concat(source2, ...sources);
}
min<O>(otherwise?: AsyncOptLazy<O>): Promise<O> {
return AsyncOptLazy.toPromise(otherwise!);
}
minBy<O>(compare: any, otherwise?: AsyncOptLazy<O>): Promise<O> {
return AsyncOptLazy.toPromise(otherwise!);
}
max<O>(otherwise?: AsyncOptLazy<O>): Promise<O> {
return AsyncOptLazy.toPromise(otherwise!);
}
maxBy<O>(compare: any, otherwise?: AsyncOptLazy<O>): Promise<O> {
return AsyncOptLazy.toPromise(otherwise!);
}
intersperse(): AsyncStream<T> {
return this;
}
async join({
start = '',
end = '',
ifEmpty = undefined,
} = {}): Promise<string> {
return undefined !== ifEmpty ? ifEmpty : start.concat(end);
}
mkGroup({
start = emptyAsyncStream as AsyncStreamSource<T>,
end = emptyAsyncStream as AsyncStreamSource<T>,
} = {}): AsyncStream.NonEmpty<T> {
return fromAsyncStreamSource(start).concat(end) as any;
}
splitOn<R>(): AsyncStream<R> {
return this as any;
}
splitWhere<R>(): AsyncStream<R> {
return this as any;
}
splitOnSlice<R>(): AsyncStream<R> {
return this as any;
}
distinctPrevious(): AsyncStream<T> {
return this;
}
window<R>(): AsyncStream<R> {
return this as any;
}
fold<R>(init: AsyncOptLazy<R>): Promise<R> {
return AsyncOptLazy.toPromise(init);
}
foldStream<R>(): AsyncStream<R> {
return this as any;
}
async reduce(shape: any): Promise<any> {
const reducer = AsyncReducer.combine(shape);
const instance = await reducer.compile();
return instance.getOutput();
}
reduceStream(): any {
return this;
}
async toArray(): Promise<[]> {
return [];
}
toString(): string {
return `AsyncStream(<empty>)`;
}
async toJSON(): Promise<ToJSON<T[], 'AsyncStream'>> {
return {
dataType: 'AsyncStream',
value: [],
};
}
}
export class FromSource<T> extends AsyncStreamBase<T> {
constructor(
readonly source: AsyncStreamSource<T>,
readonly close?: () => MaybePromise<void>
) {
super();
}
[Symbol.asyncIterator](): AsyncFastIterator<T> {
return asyncStreamSourceToIterator(this.source, this.close);
}
}
export class FromResource<T, R> extends AsyncStreamBase<T> {
constructor(
readonly open: () => MaybePromise<R>,
readonly createSource: (resource: R) => MaybePromise<AsyncStreamSource<T>>,
readonly close?: (resource: R) => MaybePromise<void>
) {
super();
}
[Symbol.asyncIterator](): AsyncFastIterator<T> {
return new FromResourceIterator(
this.open,
this.createSource,
this.close,
asyncStreamSourceHelpers
);
}
}
export const emptyAsyncStream: AsyncStream<any> = Object.freeze(
new AsyncEmptyStream()
);
export function isEmptyAsyncStreamSourceInstance(
source: AsyncStreamSource<any>
): boolean {
return (
source === emptyAsyncStream ||
isEmptyStreamSourceInstance(source as StreamSource<any>)
);
}
export function asyncStreamSourceToIterator<T>(
source: AsyncStreamSource<T>,
close?: () => MaybePromise<void>
): AsyncFastIterator<T> {
if (source instanceof Function) {
return new FromPromise(source as any, asyncStreamSourceHelpers, close);
}
if (isEmptyAsyncStreamSourceInstance(source)) {
return emptyAsyncFastIterator;
}
if (typeof source === 'string') {
return new FromIterator((source as any)[Symbol.iterator](), close);
}
if (typeof source === 'object') {
if (Symbol.asyncIterator in source) {
const iterator = (source as AsyncIterable<T>)[Symbol.asyncIterator]();
if (isAsyncFastIterator(iterator)) {
if (undefined === close) {
return iterator as AsyncFastIterator<T>;
}
if (undefined === iterator.return) {
(iterator as any).return = close;
return iterator;
}
const oldReturn = iterator.return;
(iterator as any).return = (): Promise<any> =>
Promise.all([oldReturn, close]);
return iterator;
}
return new FromAsyncIterator(iterator, close);
}
if (`asyncStream` in source) {
return asyncStreamSourceToIterator((source as any).asyncStream(), close);
}
if (Symbol.iterator in source) {
return new FromIterator((source as any)[Symbol.iterator](), close);
}
}
throw Error('unknown async stream source');
}
// prettier-ignore
export const fromAsyncStreamSource: {
<T>(source: AsyncStreamSource.NonEmpty<T>): AsyncStream.NonEmpty<T>;
<T>(source: AsyncStreamSource<T>): AsyncStream<T>;
} = <T,>(source: AsyncStreamSource<T>): any => {
if (undefined === source) return emptyAsyncStream;
if (isAsyncStream(source)) return source;
if (isEmptyAsyncStreamSourceInstance(source)) return emptyAsyncStream;
return new FromSource(source);
};
const asyncStreamSourceHelpers = {
fromAsyncStreamSource,
isEmptyAsyncStreamSourceInstance,
};
export type AsyncStreamSourceHelpers = typeof asyncStreamSourceHelpers;
export const AsyncStreamConstructorsImpl: AsyncStreamConstructors =
Object.freeze<AsyncStreamConstructors>({
of(...values) {
return new AsyncOfStream(values) as any;
},
from(...sources): any {
const [first, ...rest] = sources;
if (rest.length <= 0) {
return fromAsyncStreamSource(first);
}
const [rest1,