@rschedule/rschedule
Version:
A typescript library for working with recurring dates and events.
92 lines (72 loc) • 2.73 kB
text/typescript
import { DateAdapter } from '../date-adapter';
import { DateTime } from '../date-time';
import { IOccurrenceGenerator, IRunArgs } from '../interfaces';
import { IOperatorConfig, Operator, OperatorFnOutput } from './interface';
import {
IterableWrapper,
selectNextIterable,
streamPastEnd,
streamPastSkipToDate,
} from './utilities';
const ADD_OPERATOR_ID = Symbol.for('2898c208-e9a8-41a2-8627-2bc993ab376f');
/**
* An operator function which accepts a spread of occurrence generators
* and adds their occurrences to the output.
*
* @param streams a spread of occurrence generators
*/
export function add<T extends typeof DateAdapter>(
...streams: IOccurrenceGenerator<T>[]
): OperatorFnOutput<T> {
return (options: IOperatorConfig<T>) => new AddOperator(streams, options);
}
export class AddOperator<T extends typeof DateAdapter> extends Operator<T> {
static isAddOperator(object: unknown): object is AddOperator<any> {
return !!(super.isOperator(object) && (object as any)[ADD_OPERATOR_ID]);
}
protected readonly [ADD_OPERATOR_ID] = true;
/** Not actually used but necessary for IRunnable interface */
set(_: 'timezone', value: string | null) {
return new AddOperator(this._streams.map(stream => stream.set('timezone', value)), {
...this.config,
base: this.config.base && this.config.base.set('timezone', value),
timezone: value,
});
}
/** @internal */
*_run(args: IRunArgs = {}): IterableIterator<DateTime> {
const streams = this._streams.map(input => new IterableWrapper(input._run(args)));
if (this.config.base) {
streams.push(new IterableWrapper(this.config.base._run(args)));
}
if (streams.length === 0) return;
let stream = selectNextIterable(streams, args);
if (streamPastEnd(stream, args)) return;
while (!stream.done) {
const yieldArgs = yield this.normalizeRunOutput(stream.value);
stream.picked();
stream = selectNextIterable(streams, args);
if (yieldArgs && yieldArgs.skipToDate) {
while (
!streamPastEnd(stream, args) &&
!streamPastSkipToDate(stream, yieldArgs.skipToDate, args)
) {
stream.picked();
stream = selectNextIterable(streams, args);
}
}
if (streamPastEnd(stream, args)) return;
}
}
protected calculateIsInfinite() {
return (
(this.config.base && this.config.base.isInfinite) ||
this._streams.some(stream => stream.isInfinite)
);
}
protected calculateHasDuration() {
const streamsDuration = this._streams.every(stream => stream.hasDuration);
if (!this.config.base) return streamsDuration;
return this.config.base.hasDuration && streamsDuration;
}
}