Skip to content

Commit

Permalink
fix: improve collection types and documentation, and fix broken tests
Browse files Browse the repository at this point in the history
  • Loading branch information
vitoke committed Nov 24, 2021
1 parent 72e4849 commit 857d32d
Show file tree
Hide file tree
Showing 20 changed files with 350 additions and 308 deletions.
38 changes: 25 additions & 13 deletions deno_dist/collection-types/map/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,11 +298,11 @@ export interface RMapBase<K, V, Tp extends RMapBase.Types = RMapBase.Types>
* @example
* HashMap.of([1, 'a']).addEntries([[2, 'b']]).toArray() // => [[1, 'a'], [2, 'b']]
*/
addEntries(
entries: StreamSource.NonEmpty<readonly [K, V]>
addEntries<V2 extends V = V>(
entries: StreamSource.NonEmpty<readonly [K, V2]>
): WithKeyValue<Tp, K, V>['nonEmpty'];
addEntries(
entries: StreamSource<readonly [K, V]>
addEntries<V2 extends V = V>(
entries: StreamSource<readonly [K, V2]>
): WithKeyValue<Tp, K, V>['normal'];
/**
* Returns the collection with the given `atKey` key modified according to given `options`.
Expand Down Expand Up @@ -477,11 +477,12 @@ export namespace RMapBase {
* the tuple will be filled with the given `fillValue`.
* @typeparam O - the type of the fill value
* @typeparam I - the array of input source value types
* @typeparam K - the common key type
* @param fillValue - the value to use for the result tuple if a source does not have a certain key
* @param sources - a non-empty set of StreamSouces containing tuples of keys and values
* @example
* const m = HashMap.of([1, 'a'], [2, 'b'])
* const m2 = m.mergeAll('none', [[2, true]], HashMap.of([3, 15]))
* const m2 = HashMap.mergeAll('none', m, [[2, true]], HashMap.of([3, 15]))
* // type of m2: HashMap<number, [string, boolean | string, number | string]>
* console.log(m2.toArray())
* // => [[1, ['a', 'none', 'none']], [2, ['b', true, 'none']], [3, ['none', 'none', 15]]]
Expand Down Expand Up @@ -510,17 +511,23 @@ export namespace RMapBase {
* Returns a Map containing all keys from this map and all the given `sources` key-value stream sources,
* and as values the result of applying the given `mergeFun` to the key and all the corresponding values for each key. If a source doesn't have a key,
* the given tuple will be filled with the given `fillValue`.
* @typeparam O - the type of the fill value
* @typeparam I - the array of input source value types
* @typeparam K - the common key type
* @typeparam O - the type of the fill value
* @typeparam R - the resulting Map value type
* @param fillValue - the value to use for the result tuple if a source does not have a certain key
* @param sources - a non-empty set of StreamSouces containing tuples of keys and values
* @param mergeFun - a function that receives each key of the given sources and, if present, the corresponding source values, or the given fill value otherwise,
* and returns the result value to use in the resulting map.
* @example
* const m = HashMap.of([1, 'a'], [2, 'b'])
* const m2 = m.mergeAllWith(
* 'q',
* (key, v1, v2, v3) => `${key}${v1}${v2}${v3}`,
* const m2 = HashMap.mergeAllWith(
* m
* [[2, 'c']],
* HashMap.of([3, 'd'])
* )(
* 'q',
* (key, v1, v2, v3) => `${key}${v1}${v2}${v3}`
* )
* // type of m2: HashMap<number, string>
* console.log(m2.toArray())
Expand Down Expand Up @@ -553,10 +560,11 @@ export namespace RMapBase {
* and as values tuples of all the corresponding values for each common key. If a source doesn't have a key,
* the key will be skipped.
* @typeparam I - the array of input source value types
* @typeparam K - the common key type
* @param sources - a non-empty set of StreamSouces containing tuples of keys and values
* @example
* const m = HashMap.of([1, 'a'], [2, 'b'])
* const m2 = m.merge([[2, true]], HashMap.of([2, 15]))
* const m2 = HashMap.merge(m, [[2, true]], HashMap.of([2, 15]))
* // type of m2: HashMap<number, [string, boolean, number]>
* console.log(m2.toArray())
* // => [[2, ['b', true, 15]]]
Expand All @@ -576,15 +584,19 @@ export namespace RMapBase {
* and as values the result of applying given `mergeFun` to the key and values of all the corresponding values for each common key.
* If a source doesn't have a key, the key will be skipped.
* @typeparam I - the array of input source value types
* @typeparam K - the common key type
* @typeparam R - the resulting Map value type
* @param sources - a non-empty set of StreamSouces containing tuples of keys and values
* @param mergeFun - a function taking the key and values from this map and all sources corresponding to the key, and
* returning a value for the resulting Map.
* @param sources - a non-empty set of StreamSouces containing tuples of keys and values
* @example
* const m = HashMap.of([1, 'a'], [2, 'b'])
* const m2 = m.merge(
* (key, v1, v2) => `${key}${v1}${v2}`,
* const m2 = HashMap.mergeWith(
* m,
* [[2, true]],
* HashMap.of([2, 15])
* )(
* (key, v1, v2) => `${key}${v1}${v2}`,
* )
* // type of m2: HashMap<number, string>
* console.log(m2.toArray())
Expand Down
9 changes: 5 additions & 4 deletions deno_dist/common/async-optlazy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { OptLazy } from './internal.ts';
import type { OptLazy } from './internal.ts';

/**
* A type that is either a value T or a promise yielding a value of type T.
Expand All @@ -23,9 +23,10 @@ export namespace AsyncOptLazy {
* AsyncOptLazy.toMaybePromise(async () => 1) // => Promise(1)
* AsyncOptLazy.toMaybePromise(Promise.resolve(1)) // => Promise(1)
*/
export const toMaybePromise: <T>(
optLazy: AsyncOptLazy<T>
) => MaybePromise<T> = OptLazy;
export function toMaybePromise<T>(optLazy: AsyncOptLazy<T>): MaybePromise<T> {
if (optLazy instanceof Function) return optLazy();
return optLazy;
}

/**
* Returns the value contained in an `AsyncOptLazy` instance of type T as a promise.
Expand Down
9 changes: 5 additions & 4 deletions deno_dist/stream/async-stream/async-stream-source.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import type { MaybePromise, OptLazy } from '../../common/mod.ts';
import type { MaybePromise } from '../../common/mod.ts';
import { AsyncStream, AsyncStreamable, StreamSource } from '../internal.ts';

export type AsyncStreamSource<T> =
| AsyncStreamSource.NonEmpty<T>
| AsyncStream<T>
| (() => MaybePromise<AsyncStreamSource<T>>)
| AsyncStreamable<T>
| StreamSource<T>
| AsyncIterable<T>;

export namespace AsyncStreamSource {
export type NonEmpty<T> = OptLazy<
export type NonEmpty<T> =
| AsyncStream.NonEmpty<T>
| AsyncStreamable.NonEmpty<T>
| StreamSource.NonEmpty<T>
| (() => MaybePromise<AsyncStreamSource.NonEmpty<T>>)
>;
| (() => MaybePromise<AsyncStreamSource.NonEmpty<T>>);

export function isEmptyInstance(source: AsyncStreamSource<any>): boolean {
return (
Expand Down
112 changes: 57 additions & 55 deletions deno_dist/stream/async-stream/constructors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,18 @@ import {
ArrayNonEmpty,
AsyncOptLazy,
AsyncReducer,
MaybePromise,
OptLazy,
Reducer,
MaybePromise, Reducer
} from '../../common/mod.ts';
import {
AsyncFastIterator,
AsyncStream,
AsyncStreamSource,
Stream,
Stream
} from '../internal.ts';
import {
AsyncFastIteratorBase,
AsyncFromStream,
AsyncStreamBase,
AsyncStreamBase
} from './async-stream-custom.ts';
import { closeIters } from './utils.ts';

Expand Down Expand Up @@ -85,41 +83,40 @@ export const fromResource: {
return new FromResource(open, createSource, close);
};

/** Returns an AsyncStream with the result of applying given `zipFun` to each successive value resulting from the given `streams`.
/** Returns an AsyncStream with the result of applying given `zipFun` to each successive value resulting from the given `sources`.
* @param sources - the input async stream sources
* @param zipFun - a potentially asynchronous function taking one element from each given Stream, and returning a result value
* @param streams - the input async stream sources
* @example
* await AsyncStream.zipWith(
* async (a, b, c) => c ? a + b : a - b,
* [1, 2],
* [3, 4, 5],
* [true, false]
* )(
* async (a, b, c) => c ? a + b : a - b,
* ).toArray()
* // => [4, -2]
* @note ends the AsyncStream when any of the given streams ends
*/
export const zipWith: {
<I extends readonly unknown[], R>(
zipFun: (...values: I) => R,
...iters: { [K in keyof I]: AsyncStreamSource.NonEmpty<I[K]> } & unknown[]
): AsyncStream.NonEmpty<R>;
<I extends readonly unknown[], R>(
zipFun: (...values: I) => R,
...iters: { [K in keyof I]: AsyncStreamSource<I[K]> } & unknown[]
): AsyncStream<R>;
} = (zipFun, ...iters) => {
if (iters.some(AsyncStreamSource.isEmptyInstance)) {
return AsyncStream.empty();
}
<I extends readonly unknown[]>(
...sources: { [K in keyof I]: AsyncStreamSource.NonEmpty<I[K]> } & unknown[]
): <R>(zipFun: (...values: I) => R) => AsyncStream.NonEmpty<R>;
<I extends readonly unknown[]>(
...sources: { [K in keyof I]: AsyncStreamSource<I[K]> } & unknown[]
): <R>(zipFun: (...values: I) => R) => AsyncStream<R>;
} = (...sources): any => {
return (zipFun: any): any => {
if (sources.some(AsyncStreamSource.isEmptyInstance)) {
return AsyncStream.empty();
}

return new AsyncFromStream(
() => new AsyncZipWithIterator(iters as any, zipFun)
) as any;
return new AsyncFromStream(() => new AsyncZipWithIterator(sources, zipFun));
};
};

/**
* Returns an AsyncStream with tuples containing each successive value from the given `streams`.
* @param streams - the input async stream sources
* Returns an AsyncStream with tuples containing each successive value from the given `sources`.
* @param sources - the input async stream sources
* @example
* await AsyncStream.zip(
* [1, 2, 3],
Expand All @@ -131,56 +128,61 @@ export const zipWith: {
*/
export const zip: {
<I extends readonly unknown[]>(
...iters: { [K in keyof I]: AsyncStreamSource.NonEmpty<I[K]> } & unknown[]
...sources: { [K in keyof I]: AsyncStreamSource.NonEmpty<I[K]> } & unknown[]
): AsyncStream.NonEmpty<I>;
<I extends readonly unknown[]>(
...iters: { [K in keyof I]: AsyncStreamSource<I[K]> } & unknown[]
...sources: { [K in keyof I]: AsyncStreamSource<I[K]> } & unknown[]
): AsyncStream<I>;
} = (...iters) => zipWith(Array, ...(iters as any)) as any;
} = (...sources): any => zipWith(...sources)(Array);

/**
* Returns an AsyncStream with the result of applying given `zipFun` to each successive value resulting from the given `streams`, adding
* Returns an AsyncStream with the result of applying given `zipFun` to each successive value resulting from the given `sources`, adding
* given `fillValue` to any Streams that end before all streams have ended.
* @param sources - the input async stream sources
* @param fillValue - the `AsyncOptLazyz value to add to streams that end early
* @param zipFun - a potentially asynchronous function taking one element from each given Stream, and returning a result value
* @param streams - the input async stream sources
* @example
* await AsyncStream.zipAllWith(
* async () => 0,
* async (a, b, c) => a + b + c,
* [1, 2],
* [3, 4, 5],
* [6, 7]
* )(
* async () => 0,
* async (a, b, c) => a + b + c,
* ).toArray()
* // => [10, 13, 5]
*/
export const zipAllWith: {
<I extends readonly unknown[], O, R>(
<I extends readonly unknown[]>(
...sources: { [K in keyof I]: AsyncStreamSource.NonEmpty<I[K]> } & unknown[]
): <O, R>(
fillValue: AsyncOptLazy<O>,
zipFun: (...values: { [K in keyof I]: I[K] | O }) => MaybePromise<R>,
...streams: { [K in keyof I]: AsyncStreamSource.NonEmpty<I[K]> } & unknown[]
): AsyncStream.NonEmpty<R>;
<I extends readonly unknown[], O, R>(
fillValue: OptLazy<O>,
zipFun: (...values: { [K in keyof I]: I[K] | O }) => MaybePromise<R>,
...streams: { [K in keyof I]: AsyncStreamSource<I[K]> } & unknown[]
): AsyncStream<R>;
} = (fillValue, zipFun, ...streams) => {
if (streams.every(AsyncStreamSource.isEmptyInstance)) {
return AsyncStream.empty();
}
zipFun: (...values: { [K in keyof I]: I[K] | O }) => MaybePromise<R>
) => AsyncStream.NonEmpty<R>;
<I extends readonly unknown[]>(
...sources: { [K in keyof I]: AsyncStreamSource<I[K]> } & unknown[]
): <O, R>(
fillValue: AsyncOptLazy<O>,
zipFun: (...values: { [K in keyof I]: I[K] | O }) => MaybePromise<R>
) => AsyncStream<R>;
} = (...sources): any => {
return (fillValue: any, zipFun: any): any => {
if (sources.every(AsyncStreamSource.isEmptyInstance)) {
return AsyncStream.empty();
}

return new AsyncFromStream(
(): AsyncFastIterator<any> =>
new AsyncZipAllWithItererator(fillValue, streams, zipFun as any)
) as any;
return new AsyncFromStream(
(): AsyncFastIterator<any> =>
new AsyncZipAllWithItererator(fillValue, sources, zipFun)
);
};
};

/**
* Returns an AsyncStream with tuples containing each successive value from the given `streams`, adding given `fillValue` to any streams
* Returns an AsyncStream with tuples containing each successive value from the given `sources`, adding given `fillValue` to any streams
* that end before all streams have ended.
* @param fillValue - the `AsyncOptLazy` value to add to streams that end early
* @param streams - the input async stream sources
* @param sources - the input async stream sources
* @example
* await AsyncStream.zipAll(
* 0,
Expand All @@ -194,14 +196,14 @@ export const zipAllWith: {
export const zipAll: {
<I extends readonly unknown[], O>(
fillValue: AsyncOptLazy<O>,
...streams: { [K in keyof I]: AsyncStreamSource.NonEmpty<I[K]> } & unknown[]
...sources: { [K in keyof I]: AsyncStreamSource.NonEmpty<I[K]> } & unknown[]
): AsyncStream.NonEmpty<{ [K in keyof I]: I[K] | O }>;
<I extends readonly unknown[], O>(
fillValue: AsyncOptLazy<O>,
...streams: { [K in keyof I]: AsyncStreamSource<I[K]> } & unknown[]
...sources: { [K in keyof I]: AsyncStreamSource<I[K]> } & unknown[]
): AsyncStream<{ [K in keyof I]: I[K] | O }>;
} = (fillValue, ...streams) =>
zipAllWith(fillValue, Array, ...(streams as any)) as any;
} = (fillValue, ...sources): any =>
zipAllWith(...(sources as any))(fillValue, Array);

/**
* Returns an AsyncStream concatenating the given `source` AsyncStreamSource containing StreamSources.
Expand Down

0 comments on commit 857d32d

Please sign in to comment.