import { BindingCount } from "../bindings/binding_count" ; import * as ERROR_MSGS from "../constants/error_msgs" ; import { BindingTypeEnum , TargetTypeEnum } from "../constants/literal_types" ; import * as METADATA_KEY from "../constants/metadata_keys" ; import { interfaces } from "../interfaces/interfaces" ; import { isStackOverflowExeption } from "../utils/exceptions" ; import { circularDependencyToException , getServiceIdentifierAsString , listMetadataForTarget , listRegisteredBindingsForServiceIdentifier } from "../utils/serialization" ; import { Context } from "./context" ; import { Metadata } from "./metadata" ; import { Plan } from "./plan" ; import { getBaseClassDependencyCount , getDependencies , getFunctionName } from "./reflection_utils" ; import { Request } from "./request" ; import { Target } from "./target" ; function getBindingDictionary ( cntnr ) { return ( cntnr as unknown as { _bindingDictionary : interfaces . Lookup < interfaces . Binding < unknown > > ; } ) . _bindingDictionary ; } function _createTarget ( isMultiInject , targetType , serviceIdentifier , name , key ? , value ? ) { const metadataKey = isMultiInject ? METADATA_KEY . MULTI_INJECT_TAG : METADATA_KEY . INJECT_TAG ; const injectMetadata = new Metadata ( metadataKey , serviceIdentifier ) ; const target = new Target ( targetType , name , serviceIdentifier , injectMetadata ) ; if ( key !== undefined ) { const tagMetadata = new Metadata ( key , value ) ; target . metadata . push ( tagMetadata ) ; } return target ; } function _getActiveBindings ( metadataReader , avoidConstraints , context , parentRequest , target ) { let bindings = getBindings ( context . container , target . serviceIdentifier ) ; let activeBindings = [ ] ; if ( bindings . length === BindingCount . NoBindingsAvailable && context . container . options . autoBindInjectable && typeof target . serviceIdentifier === "function" && metadataReader . getConstructorMetadata ( target . serviceIdentifier ) . compilerGeneratedMetadata ) { context . container . bind ( target . serviceIdentifier ) . toSelf ( ) ; bindings = getBindings ( context . container , target . serviceIdentifier ) ; } if ( ! avoidConstraints ) { activeBindings = bindings . filter ( ( binding ) => { const request = new Request ( binding . serviceIdentifier , context , parentRequest , binding , target ) ; return binding . constraint ( request ) ; } ) ; } else { activeBindings = bindings ; } _validateActiveBindingCount ( target . serviceIdentifier , activeBindings , target , context . container ) ; return activeBindings ; } function _validateActiveBindingCount ( serviceIdentifier , bindings , target , container ) { switch ( bindings . length ) { case BindingCount . NoBindingsAvailable : if ( target . isOptional ( ) ) { return bindings ; } else { const serviceIdentifierString = getServiceIdentifierAsString ( serviceIdentifier ) ; let msg = ERROR_MSGS . NOT_REGISTERED ; msg += listMetadataForTarget ( serviceIdentifierString , target ) ; msg += listRegisteredBindingsForServiceIdentifier ( container , serviceIdentifierString , getBindings ) ; throw new Error ( msg ) ; } case BindingCount . OnlyOneBindingAvailable : return bindings ; case BindingCount . MultipleBindingsAvailable : default : if ( ! target . isArray ( ) ) { const serviceIdentifierString = getServiceIdentifierAsString ( serviceIdentifier ) ; let msg = `${ ERROR_MSGS . AMBIGUOUS_MATCH } ${ serviceIdentifierString }` ; msg += listRegisteredBindingsForServiceIdentifier ( container , serviceIdentifierString , getBindings ) ; throw new Error ( msg ) ; } else { return bindings ; } } } function _createSubRequests ( metadataReader , avoidConstraints , serviceIdentifier , context , parentRequest , target ) { let activeBindings ; let childRequest ; if ( parentRequest === null ) { activeBindings = _getActiveBindings ( metadataReader , avoidConstraints , context , null , target ) ; childRequest = new Request ( serviceIdentifier , context , null , activeBindings , target ) ; const thePlan = new Plan ( context , childRequest ) ; context . addPlan ( thePlan ) ; } else { activeBindings = _getActiveBindings ( metadataReader , avoidConstraints , context , parentRequest , target ) ; childRequest = parentRequest . addChildRequest ( target . serviceIdentifier , activeBindings , target ) ; } activeBindings . forEach ( ( binding ) => { let subChildRequest = null ; if ( target . isArray ( ) ) { subChildRequest = childRequest . addChildRequest ( binding . serviceIdentifier , binding , target ) ; } else { if ( binding . cache ) { return ; } subChildRequest = childRequest ; } if ( binding . type === BindingTypeEnum . Instance && binding . implementationType !== null ) { const dependencies = getDependencies ( metadataReader , binding . implementationType ) ; if ( ! context . container . options . skipBaseClassChecks ) { const baseClassDependencyCount = getBaseClassDependencyCount ( metadataReader , binding . implementationType ) ; if ( dependencies . length < baseClassDependencyCount ) { const error = ERROR_MSGS . ARGUMENTS_LENGTH_MISMATCH ( getFunctionName ( binding . implementationType ) ) ; throw new Error ( error ) ; } } dependencies . forEach ( ( dependency ) => { _createSubRequests ( metadataReader , false , dependency . serviceIdentifier , context , subChildRequest , dependency ) ; } ) ; } } ) ; } function getBindings < T > ( container , serviceIdentifier ) { let bindings = [ ] ; const bindingDictionary = getBindingDictionary ( container ) ; if ( bindingDictionary . hasKey ( serviceIdentifier ) ) { bindings = bindingDictionary . get ( serviceIdentifier ) as interfaces . Binding < T > [ ] ; } else if ( container . parent !== null ) { bindings = getBindings < T > ( container . parent , serviceIdentifier ) ; } return bindings ; } function plan ( metadataReader , container , isMultiInject , targetType , serviceIdentifier , key ? , value ? , avoidConstraints = false ) { const context = new Context ( container ) ; const target = _createTarget ( isMultiInject , targetType , serviceIdentifier , "" , key , value ) ; try { _createSubRequests ( metadataReader , avoidConstraints , serviceIdentifier , context , null , target ) ; return context ; } catch ( error ) { if ( isStackOverflowExeption ( error ) ) { circularDependencyToException ( context . plan . rootRequest ) ; } throw error ; } } function createMockRequest ( container , serviceIdentifier , key , value ) { const target = new Target ( TargetTypeEnum . Variable , "" , serviceIdentifier , new Metadata ( key , value ) ) ; const context = new Context ( container ) ; const request = new Request ( serviceIdentifier , context , null , [ ] , target ) ; return request ; } export { plan , createMockRequest , getBindingDictionary } ;