import { IResolvableTree } from './interfaces'; import { AdvancedAbortController, CancellablePromise, IAdvancedAbortSignal, ICancellablePromise, IsAdvancedAbortSignal, NormalizeICancellablePromiseOptions, TAbortStrategy } from '@lifaon/observables'; import { IResolvableTreeResolveCallbackOptions, TResolvableTreeResolveCallback } from './types'; import { FreezeObjectProperties } from '../../misc/helpers/object/freeze-property'; import { PartiallyRequiredAndReadonly } from '../../misc/types/partialy-required-and-readonly'; /** FUNCTIONS **/ /* -- NORMALIZE -- */ /* IResolvableTreeResolveCallbackOptions */ export function NormalizeIResolvableTreeResolveCallbackOptionsSignal(signal?: IAdvancedAbortSignal): IAdvancedAbortSignal { if (signal === void 0) { return new AdvancedAbortController().signal; } else if (IsAdvancedAbortSignal(signal)) { return signal; } else { throw new TypeError(`Expected void or AdvancedAbortSignal as signal`); } } export function NormalizeIResolvableTreeResolveCallbackOptionsStrategy(strategy?: TStrategy): TStrategy { if (strategy === void 0) { return 'never' as TStrategy; } else if (['never', 'resolve', 'reject'].includes(strategy)) { return strategy; } else { throw new TypeError(`Expected void, 'never', 'resolve' or 'reject' as strategy`); } } export type IResolvableTreeResolveCallbackNormalizedOptions = PartiallyRequiredAndReadonly, 'strategy' | 'signal'>; export function NormalizeIResolvableTreeResolveCallbackOptions( options: IResolvableTreeResolveCallbackOptions ): IResolvableTreeResolveCallbackNormalizedOptions { return FreezeObjectProperties(NormalizeICancellablePromiseOptions(options), ['signal', 'strategy'] as ('signal'| 'strategy')[]); } /** HELPERS **/ export interface IResolveResolvableTreesOneByOneReducible { reduce(callback: (previousValue: U, currentValue: IResolvableTree, ...args: any[]) => U, initialValue: U): U; } /** * For each 'tress', resolve it. * - if it resolves with a path not empty, return this path * - else, go to next tree and repeat */ export function ResolveResolvableTreesOneByOne( trees: IResolveResolvableTreesOneByOneReducible, options: IResolvableTreeResolveCallbackOptions, ): ICancellablePromise { return trees.reduce((promise: ICancellablePromise, tree: IResolvableTree) => { return promise.then((path: IResolvableTree[]) => { return (path.length === 0) ? tree.resolve(options) : path; }); }, CancellablePromise.resolve([], options)); } /* CREATE RESOLVER */ export interface ICreateResolvableTreeResolverOptionsResolveCallbackReturnedValue { resolved: boolean; childrenOptions?: IResolvableTreeResolveCallbackOptions; resolvePathOptions?: IResolvableTreeResolveCallbackOptions; } export type TCreateResolvableTreeResolverOptionsResolveCallback = ( this: IResolvableTree, options: IResolvableTreeResolveCallbackNormalizedOptions ) => ICancellablePromise, TStrategy>; export type TCreateResolvableTreeResolverOptionsResolvePathCallback = ( this: IResolvableTree, options: IResolvableTreeResolveCallbackNormalizedOptions, path: IResolvableTree[] ) => ICancellablePromise; export interface ICreateResolvableTreeResolverOptions { resolve?: TCreateResolvableTreeResolverOptionsResolveCallback; resolvePath?: TCreateResolvableTreeResolverOptionsResolvePathCallback; } export function CreateResolvableTreeResolver( resolverOptions: ICreateResolvableTreeResolverOptions = {} ): TResolvableTreeResolveCallback { const resolve: TCreateResolvableTreeResolverOptionsResolveCallback = (resolverOptions.resolve === void 0) ? ( options: IResolvableTreeResolveCallbackNormalizedOptions ): ICancellablePromise, TStrategy> => { return CancellablePromise.resolve, TStrategy>({ resolved: true }, options); } : resolverOptions.resolve; const resolvePath: TCreateResolvableTreeResolverOptionsResolvePathCallback = (resolverOptions.resolvePath === void 0) ? function ( this: IResolvableTree, options: IResolvableTreeResolveCallbackNormalizedOptions, path: IResolvableTree[] ): ICancellablePromise { const instance: IResolvableTree = this; return CancellablePromise.try(() => { return ((instance.children.length > 0) && (path.length === 0)) ? [] : [instance].concat(path); }, options); } : resolverOptions.resolvePath; return function ( this: IResolvableTree, options: IResolvableTreeResolveCallbackOptions ): ICancellablePromise { const instance: IResolvableTree = this; const normalizedOptions: IResolvableTreeResolveCallbackNormalizedOptions = NormalizeIResolvableTreeResolveCallbackOptions(options); return (resolve.call(instance, normalizedOptions) as ICancellablePromise, TStrategy>) .then((result: ICreateResolvableTreeResolverOptionsResolveCallbackReturnedValue) => { if (result.resolved) { const childrenOptions: IResolvableTreeResolveCallbackOptions = (result.childrenOptions === void 0) ? normalizedOptions : result.childrenOptions; return ResolveResolvableTreesOneByOne( instance.children, childrenOptions ) .then((path: IResolvableTree[]) => { const resolvePathOptions: IResolvableTreeResolveCallbackOptions = (result.resolvePathOptions === void 0) ? normalizedOptions : result.resolvePathOptions; return (resolvePath.call(instance, resolvePathOptions, path) as ICancellablePromise); }); } else { return []; } }); }; }