Start routing and pipeline rewrite
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -7,7 +7,7 @@ import {
|
||||
} from './Collection'
|
||||
import {Iterable, StopIteration} from './Iterable'
|
||||
import {applyWhere, WhereOperator} from './where'
|
||||
import {AsyncPipe, Pipe} from '../support/Pipe'
|
||||
import {AsyncPipe, Pipeline} from '../support/Pipe'
|
||||
type AsyncCollectionComparable<T> = CollectionItem<T>[] | Collection<T> | AsyncCollection<T>
|
||||
type AsyncKeyFunction<T, T2> = (item: CollectionItem<T>, index: number) => CollectionItem<T2> | Promise<CollectionItem<T2>>
|
||||
type AsyncCollectionFunction<T, T2> = (items: AsyncCollection<T>) => T2
|
||||
@@ -798,19 +798,16 @@ export class AsyncCollection<T> {
|
||||
return this.storedItems.range(start, end)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the function, passing this collection to it.
|
||||
* @param {AsyncCollectionFunction} func
|
||||
*/
|
||||
pipeTo<T2>(func: AsyncCollectionFunction<T, T2>): any {
|
||||
return func(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new Pipe of this collection.
|
||||
*/
|
||||
pipe(): Pipe<AsyncCollection<T>> {
|
||||
return Pipe.wrap(this)
|
||||
pipeTo<TOut>(pipeline: Pipeline<this, TOut>): TOut {
|
||||
return pipeline.apply(this)
|
||||
}
|
||||
|
||||
/** Build and apply a pipeline. */
|
||||
pipe<TOut>(builder: (pipeline: Pipeline<this, this>) => Pipeline<this, TOut>): TOut {
|
||||
return builder(Pipeline.id()).apply(this)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {AsyncPipe, Pipe} from '../support/Pipe'
|
||||
import {AsyncPipe, Pipeline} from '../support/Pipe'
|
||||
|
||||
type CollectionItem<T> = T
|
||||
type MaybeCollectionItem<T> = CollectionItem<T> | undefined
|
||||
@@ -822,8 +822,13 @@ class Collection<T> {
|
||||
/**
|
||||
* Return a new Pipe of this collection.
|
||||
*/
|
||||
pipe(): Pipe<Collection<T>> {
|
||||
return Pipe.wrap(this)
|
||||
pipeTo<TOut>(pipeline: Pipeline<this, TOut>): TOut {
|
||||
return pipeline.apply(this)
|
||||
}
|
||||
|
||||
/** Build and apply a pipeline. */
|
||||
pipe<TOut>(builder: (pipeline: Pipeline<this, this>) => Pipeline<this, TOut>): TOut {
|
||||
return builder(Pipeline.id()).apply(this)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {collect} from '../collection/Collection'
|
||||
import {InvalidJSONStateError, JSONState, Rehydratable} from './Rehydratable'
|
||||
import {Pipe} from './Pipe'
|
||||
import {Pipeline} from './Pipe'
|
||||
|
||||
/**
|
||||
* A class for building and working with messages grouped by keys.
|
||||
@@ -131,9 +131,14 @@ export class Messages implements Rehydratable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new Pipe object wrapping this instance.
|
||||
* Return a new Pipe of this collection.
|
||||
*/
|
||||
pipe(): Pipe<Messages> {
|
||||
return Pipe.wrap<Messages>(this)
|
||||
pipeTo<TOut>(pipeline: Pipeline<this, TOut>): TOut {
|
||||
return pipeline.apply(this)
|
||||
}
|
||||
|
||||
/** Build and apply a pipeline. */
|
||||
pipe<TOut>(builder: (pipeline: Pipeline<this, this>) => Pipeline<this, TOut>): TOut {
|
||||
return builder(Pipeline.id()).apply(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/**
|
||||
* A closure that maps a given pipe item to a different type.
|
||||
*/
|
||||
import {Awaitable} from './types'
|
||||
import {Awaitable, Maybe} from './types'
|
||||
|
||||
export type PipeOperator<T, T2> = (subject: T) => T2
|
||||
|
||||
/**
|
||||
* A closure that maps a given pipe item to an item of the same type.
|
||||
*/
|
||||
export type ReflexivePipeOperator<T> = (subject: T) => T
|
||||
export type ReflexivePipeOperator<T> = (subject: T) => Maybe<T>
|
||||
|
||||
/**
|
||||
* A condition or condition-resolving function for pipe methods.
|
||||
@@ -19,48 +19,14 @@ export type PipeCondition<T> = boolean | ((subject: T) => boolean)
|
||||
* A class for writing chained/conditional operations in a data-flow manner.
|
||||
*
|
||||
* This is useful when you need to do a series of operations on an object, perhaps conditionally.
|
||||
*
|
||||
* @example
|
||||
* Say we have a Collection of items, and want to apply some transformations and filtering based on arguments:
|
||||
*
|
||||
* ```typescript
|
||||
* const collection = collect([1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||
*
|
||||
* function transform(collection, evensOnly = false, returnEntireCollection = false) {
|
||||
* return Pipe.wrap(collection)
|
||||
* .when(evensOnly, coll => {
|
||||
* return coll.filter(x => !(x % 2))
|
||||
* })
|
||||
* .unless(returnEntireCollection, coll => {
|
||||
* return coll.take(3)
|
||||
* })
|
||||
* .tap(coll => {
|
||||
* return coll.map(x => x * 2))
|
||||
* })
|
||||
* .get()
|
||||
* }
|
||||
*
|
||||
* transform(collection) // => Collection[2, 4, 6]
|
||||
*
|
||||
* transform(collection, true) // => Collection[4, 8, 12]
|
||||
*
|
||||
* transform(collection, false, true) // => Collection[2, 4, 6, 8, 10, 12, 14, 16, 18]
|
||||
* ```
|
||||
*/
|
||||
export class Pipe<T> {
|
||||
/**
|
||||
* Return a new Pipe containing the given subject.
|
||||
* @param subject
|
||||
*/
|
||||
static wrap<subjectType>(subject: subjectType): Pipe<subjectType> {
|
||||
return new Pipe<subjectType>(subject)
|
||||
export class Pipeline<TIn, TOut> {
|
||||
static id<T>(): Pipeline<T, T> {
|
||||
return new Pipeline(x => x)
|
||||
}
|
||||
|
||||
constructor(
|
||||
/**
|
||||
* The item being operated on.
|
||||
*/
|
||||
private subject: T,
|
||||
protected readonly factory: (TIn: TIn) => TOut,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -75,17 +41,22 @@ export class Pipe<T> {
|
||||
*
|
||||
* @param op
|
||||
*/
|
||||
tap<T2>(op: PipeOperator<T, T2>): Pipe<T2> {
|
||||
return new Pipe(op(this.subject))
|
||||
tap<T2>(op: PipeOperator<TOut, T2>): Pipeline<TIn, T2> {
|
||||
return new Pipeline((val: TIn) => {
|
||||
return op(this.factory(val))
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Like tap, but always returns the original pipe.
|
||||
* Like tap, but always returns the original pipe type.
|
||||
* @param op
|
||||
*/
|
||||
peek<T2>(op: PipeOperator<T, T2>): this {
|
||||
op(this.subject)
|
||||
return this
|
||||
peek<T2>(op: PipeOperator<TOut, T2>): Pipeline<TIn, TOut> {
|
||||
return new Pipeline((val: TIn) => {
|
||||
const nextVal = this.factory(val)
|
||||
op(nextVal)
|
||||
return nextVal
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,14 +66,20 @@ export class Pipe<T> {
|
||||
* @param check
|
||||
* @param op
|
||||
*/
|
||||
when(check: PipeCondition<T>, op: ReflexivePipeOperator<T>): Pipe<T> {
|
||||
if (
|
||||
(typeof check === 'function' && check(this.subject))
|
||||
|| (typeof check !== 'function' && check) ) {
|
||||
return Pipe.wrap(op(this.subject))
|
||||
}
|
||||
when(check: PipeCondition<TOut>, op: ReflexivePipeOperator<TOut>): Pipeline<TIn, TOut> {
|
||||
return new Pipeline((val: TIn) => {
|
||||
const nextVal = this.factory(val)
|
||||
if ( this.checkCondition(check, nextVal) ) {
|
||||
const appliedVal = op(nextVal)
|
||||
if ( typeof appliedVal === 'undefined' ) {
|
||||
return nextVal
|
||||
}
|
||||
|
||||
return this
|
||||
return appliedVal
|
||||
}
|
||||
|
||||
return nextVal
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,42 +89,32 @@ export class Pipe<T> {
|
||||
* @param check
|
||||
* @param op
|
||||
*/
|
||||
unless(check: PipeCondition<T>, op: ReflexivePipeOperator<T>): Pipe<T> {
|
||||
if (
|
||||
(typeof check === 'function' && check(this.subject))
|
||||
|| (typeof check !== 'function' && check) ) {
|
||||
return this
|
||||
}
|
||||
unless(check: PipeCondition<TOut>, op: ReflexivePipeOperator<TOut>): Pipeline<TIn, TOut> {
|
||||
return new Pipeline((val: TIn) => {
|
||||
const nextVal = this.factory(val)
|
||||
if ( !this.checkCondition(check, nextVal) ) {
|
||||
const appliedVal = op(nextVal)
|
||||
if ( typeof appliedVal === 'undefined' ) {
|
||||
return nextVal
|
||||
}
|
||||
|
||||
return Pipe.wrap(op(this.subject))
|
||||
return appliedVal
|
||||
}
|
||||
|
||||
return nextVal
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of `unless()`.
|
||||
* @param check
|
||||
* @param op
|
||||
* Apply the pipeline to an input.
|
||||
*/
|
||||
whenNot(check: PipeCondition<T>, op: ReflexivePipeOperator<T>): Pipe<T> {
|
||||
return this.unless(check, op)
|
||||
apply(input: TIn): TOut {
|
||||
return this.factory(input)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the item in the pipe.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* Pipe.wrap(4).get() // => 4
|
||||
* ```
|
||||
*/
|
||||
get(): T {
|
||||
return this.subject
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an AsyncPipe with the current item in the pipe.
|
||||
*/
|
||||
async(): AsyncPipe<T> {
|
||||
return AsyncPipe.wrap<T>(this.subject)
|
||||
protected checkCondition(check: PipeCondition<TOut>, val: TOut): boolean {
|
||||
return (typeof check === 'function' && check(val))
|
||||
|| (typeof check !== 'function' && check)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,13 +247,6 @@ export class AsyncPipe<T> {
|
||||
return this.subject()
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the value and return it in a sync `Pipe` instance.
|
||||
*/
|
||||
async sync(): Promise<Pipe<T>> {
|
||||
return Pipe.wrap<T>(await this.subject())
|
||||
}
|
||||
|
||||
/** Get the transformed value from the pipe. Allows awaiting the pipe directly. */
|
||||
then(): Promise<T> {
|
||||
return this.resolve()
|
||||
|
||||
@@ -5,7 +5,7 @@ import * as mime from 'mime-types'
|
||||
import {FileNotFoundError, Filesystem} from './path/Filesystem'
|
||||
import {Collection} from '../collection/Collection'
|
||||
import {Readable, Writable} from 'stream'
|
||||
import {Pipe} from './Pipe'
|
||||
import {Pipeline} from './Pipe'
|
||||
|
||||
/**
|
||||
* An item that could represent a path.
|
||||
@@ -533,8 +533,15 @@ export class UniversalPath {
|
||||
return false
|
||||
}
|
||||
|
||||
/** Get a new Pipe instance wrapping this. */
|
||||
toPipe(): Pipe<UniversalPath> {
|
||||
return Pipe.wrap(this)
|
||||
/**
|
||||
* Return a new Pipe of this collection.
|
||||
*/
|
||||
pipeTo<TOut>(pipeline: Pipeline<this, TOut>): TOut {
|
||||
return pipeline.apply(this)
|
||||
}
|
||||
|
||||
/** Build and apply a pipeline. */
|
||||
pipe<TOut>(builder: (pipeline: Pipeline<this, this>) => Pipeline<this, TOut>): TOut {
|
||||
return builder(Pipeline.id()).apply(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,28 @@ export type Awaitable<T> = T | Promise<T>
|
||||
/** Type alias for something that may be undefined. */
|
||||
export type Maybe<T> = T | undefined
|
||||
|
||||
export type Either<T1, T2> = Left<T1> | Right<T2>
|
||||
|
||||
export type Left<T> = [T, undefined]
|
||||
|
||||
export type Right<T> = [undefined, T]
|
||||
|
||||
export function isLeft<T1, T2>(what: Either<T1, T2>): what is Left<T1> {
|
||||
return typeof what[1] === 'undefined'
|
||||
}
|
||||
|
||||
export function isRight<T1, T2>(what: Either<T1, T2>): what is Right<T2> {
|
||||
return typeof what[0] === 'undefined'
|
||||
}
|
||||
|
||||
export function left<T>(what: T): Left<T> {
|
||||
return [what, undefined]
|
||||
}
|
||||
|
||||
export function right<T>(what: T): Right<T> {
|
||||
return [undefined, what]
|
||||
}
|
||||
|
||||
/** Type alias for a callback that accepts a typed argument. */
|
||||
export type ParameterizedCallback<T> = ((arg: T) => any)
|
||||
|
||||
@@ -30,3 +52,7 @@ export function hasOwnProperty<X extends {}, Y extends PropertyKey>(obj: X, prop
|
||||
export interface TypeTag<S extends string> {
|
||||
readonly __typeTag: S
|
||||
}
|
||||
|
||||
export type PrefixTypeArray<T, TArr extends unknown[]> = [T, ...TArr]
|
||||
export type SuffixTypeArray<TArr extends unknown[], T> = [...TArr, T]
|
||||
export type TypeArraySignature<TArr extends unknown[], TReturn> = (...params: TArr) => TReturn
|
||||
|
||||
Reference in New Issue
Block a user