import { DependencyIdentifier } from './dependencyIdentifier' ; import { Ctor , DependencyItem , prettyPrintIdentifier } from './dependencyItem' ; import { checkQuantity , retrieveQuantity } from './dependencyQuantity' ; import { Disposable , isDisposable } from './dispose' ; import { Quantity } from './types' ; import { RediError } from './error' ; export type DependencyPair < T > = [ DependencyIdentifier < T > , DependencyItem < T > ] ; export type DependencyClass < T > = [ Ctor < T > ] ; export type Dependency < T = any > = DependencyPair < T > | DependencyClass < T > ; export function isBareClassDependency < T > ( thing ) { return thing . length === 1 ; } export class DependencyNotFoundError extends RediError { constructor ( id ) { const msg = `Cannot find "${ prettyPrintIdentifier ( id ) }" registered by any injector.` ; super ( msg ) ; } } internal export class DependencyCollection implements Disposable { private readonly dependencyMap = new Map < DependencyIdentifier < any > , DependencyItem < any > [ ] > ( ) ; constructor ( private readonly dependencies ) { this . normalizeDependencies ( dependencies ) . map ( ( pair ) => this . add ( pair [ 0 ] , pair [ 1 ] ) ) ; } public add < T > ( ctor ) ; public add < T > ( id , val ) ; public add < T > ( ctorOrId , val ? ) { if ( typeof val === 'undefined' ) { val = { useClass : ctorOrId as Ctor < T > , lazy : false } ; } let arr = this . dependencyMap . get ( ctorOrId ) ; if ( typeof arr === 'undefined' ) { arr = [ ] ; this . dependencyMap . set ( ctorOrId , arr ) ; } arr . push ( val ) ; } public delete < T > ( id ) { this . dependencyMap . delete ( id ) ; } public get < T > ( id ) ; public get < T > ( id , quantity ) ; public get < T > ( id , quantity ) ; public get < T > ( id , quantity ) ; public get < T > ( id , quantity ) ; public get < T > ( id , quantity = Quantity . REQUIRED ) { const ret = this . dependencyMap . get ( id ) ! ; checkQuantity ( id , quantity , ret . length ) ; return retrieveQuantity ( quantity , ret ) ; } public has < T > ( id ) { return this . dependencyMap . has ( id ) ; } public append ( dependencies ) { this . normalizeDependencies ( dependencies ) . forEach ( ( pair ) => this . add ( pair [ 0 ] , pair [ 1 ] ) ) ; } public dispose ( ) { this . dependencyMap . clear ( ) ; } /**
     * normalize dependencies to `DependencyItem`
     */ private normalizeDependencies ( dependencies ) { return dependencies . map ( ( dependency ) => { const id = dependency [ 0 ] ; let val ; if ( isBareClassDependency ( dependency ) ) { val = { useClass : dependency [ 0 ] , lazy : false , } ; } else { val = dependency [ 1 ] ; } return [ id , val ] ; } ) ; } } internal export class ResolvedDependencyCollection implements Disposable { private readonly resolvedDependencies = new Map < DependencyIdentifier < any > , any [ ] > ( ) ; public add < T > ( id , val ) { let arr = this . resolvedDependencies . get ( id ) ; if ( typeof arr === 'undefined' ) { arr = [ ] ; this . resolvedDependencies . set ( id , arr ) ; } arr . push ( val ) ; } public has < T > ( id ) { return this . resolvedDependencies . has ( id ) ; } public delete < T > ( id ) { if ( this . resolvedDependencies . has ( id ) ) { const things = this . resolvedDependencies . get ( id ) ! ; things . forEach ( ( t ) => ( isDisposable ( t ) ? t . dispose ( ) : void 0 ) ) ; this . resolvedDependencies . delete ( id ) ; } } public get < T > ( id ) ; public get < T > ( id , quantity ) ; public get < T > ( id , quantity ) ; public get < T > ( id , quantity ) ; public get < T > ( id , quantity ) ; public get < T > ( id , quantity = Quantity . REQUIRED ) { const ret = this . resolvedDependencies . get ( id ) ; if ( ! ret ) { throw new DependencyNotFoundError ( id ) ; } checkQuantity ( id , quantity , ret . length ) ; if ( quantity === Quantity . MANY ) { return ret ; } else { return ret [ 0 ] ; } } public dispose ( ) { Array . from ( this . resolvedDependencies . values ( ) ) . forEach ( ( items ) => { items . forEach ( ( item ) => ( isDisposable ( item ) ? item . dispose ( ) : void 0 ) ) ; } ) ; this . resolvedDependencies . clear ( ) ; } }