import { ArgumentError } from '../basic-utilities'; import { DateAdapter } from '../date-adapter'; import { DateTime } from '../date-time'; import { IDataContainer, IOccurrenceGenerator, OccurrenceGenerator } from '../interfaces'; import { CollectionIterator, ICollectionsArgs, ICollectionsRunArgs, IOccurrencesArgs, OccurrenceIterator, } from '../iterators'; import { add, OccurrenceStream, OperatorFnOutput, pipeFn } from '../operators'; const CALENDAR_ID = Symbol.for('5e83caab-8318-43d9-bf3d-cb24fe152246'); export class Calendar extends OccurrenceGenerator implements IDataContainer { /** * Similar to `Array.isArray()`, `isCalendar()` provides a surefire method * of determining if an object is a `Calendar` by checking against the * global symbol registry. */ static isCalendar(object: unknown): object is Calendar { return !!(object && typeof object === 'object' && (object as any)[CALENDAR_ID]); } readonly schedules: ReadonlyArray> = []; /** * Convenience property for holding arbitrary data. Accessible on individual DateAdapters * generated by this `Calendar` object via the `DateAdapter#generators` property. Unlike * the rest of the `Calendar` object, the data property is mutable. */ data!: D; pipe: (...operatorFns: OperatorFnOutput[]) => OccurrenceStream = pipeFn(this); readonly isInfinite: boolean; readonly hasDuration: boolean; protected readonly [CALENDAR_ID] = true; constructor( args: { schedules?: ReadonlyArray> | IOccurrenceGenerator; data?: D; dateAdapter?: T; timezone?: string | null; maxDuration?: number; } = {}, ) { super(args); this.data = args.data as D; if (args.schedules) { this.schedules = Array.isArray(args.schedules) ? args.schedules : [args.schedules]; this.schedules = this.schedules.map(schedule => schedule.set('timezone', this.timezone)); } this.isInfinite = this.schedules.some(schedule => schedule.isInfinite); this.hasDuration = this.schedules.every(schedule => schedule.hasDuration); } occurrences( args: IOccurrencesArgs = {}, ): OccurrenceIterator[]]> { return new OccurrenceIterator(this, this.normalizeOccurrencesArgs(args)); } collections( args: ICollectionsArgs = {}, ): CollectionIterator[]]> { return new CollectionIterator(this, this.normalizeCollectionsArgs(args)); } set( prop: 'timezone', value: string | null, options?: { keepLocalTime?: boolean }, ): Calendar; set( prop: 'schedules', value: ReadonlyArray> | IOccurrenceGenerator, ): Calendar; set( prop: 'timezone' | 'schedules', value: ReadonlyArray> | IOccurrenceGenerator | string | null, options?: { keepLocalTime?: boolean }, ) { if (prop === 'timezone') { return new Calendar({ schedules: this.schedules.map(schedule => schedule.set( prop, value as string | null, options as { keepLocalTime?: boolean } | undefined, ), ), data: this.data, dateAdapter: this.dateAdapter, timezone: value as string | null, }); } else if (prop === 'schedules') { return new Calendar({ schedules: Array.isArray(value) ? (value as IOccurrenceGenerator[]) : [value as IOccurrenceGenerator], data: this.data, dateAdapter: this.dateAdapter, timezone: this.timezone, }); } throw new ArgumentError('Unknown value for `prop`: ' + `"${prop}"`); } /** @internal */ *_run(args: ICollectionsRunArgs = {}): IterableIterator { const count = args.take; delete args.take; let iterator: IterableIterator; switch (this.schedules.length) { case 0: return; case 1: iterator = this.schedules[0]._run(args); break; default: iterator = new OccurrenceStream({ operators: [add(...this.schedules)], dateAdapter: this.dateAdapter, timezone: this.timezone, })._run(args); break; } let date = iterator.next().value; let index = 0; while (date && (count === undefined || count > index)) { date.generators.unshift(this); const yieldArgs = yield this.normalizeRunOutput(date); date = iterator.next(yieldArgs).value; index++; } } }