/**
* @module libflitter/app/Analyzer
*/
const Unit = require('../Unit')
const UnitStaticDependencyError = require('../errors/UnitStaticDependencyError')
const error_context = require('../errors/error_context.fn')
/**
* Analyzes various aspects of the application and its dependencies.
*/
class Analyzer {
constructor(app, units) {
/**
* The application in question.
* @type {module:libflitter/app/FlitterApp~FlitterApp}
*/
this.app = app
/**
* Collection of unit definitions used by the application.
* @type {object}
*/
this.units = units
}
/**
* Check the services and dependencies of each unit and ensure that none request
* a service that has not been provided by the time the unit starts.
*/
check_dependencies() {
const available_services = ['app']
for ( const UnitClass of Object.values(this.units) ) {
for ( const service of UnitClass.services ) {
if (!available_services.includes(service)) {
const error = new UnitStaticDependencyError()
error.unit(UnitClass.name).required(service)
throw error_context(error, {
UnitClass,
dependent_service: service,
})
}
}
available_services.push(UnitClass.name)
if ( UnitClass.provides ) UnitClass.provides.forEach(s => available_services.push(s))
}
}
/**
* Get a list of missing dependency names for the provided service.
* @param {module:flitter-di/src/Service~Service} service
* @returns {Array<string>}
*/
get_missing_dependencies(service) {
// Get a list of services that the service depends on
const service_dependencies = service.constructor.services
try {
// Check those dependencies
const missing_deps = []
for (const dep_name of service_dependencies) {
const dep_class = this.app.di().get(dep_name)
if (!dep_class) {
missing_deps.push(dep_name)
continue
}
// If the dep is a unit, make sure it's in the RUNNING state
if (dep_class instanceof Unit) {
if (dep_class.status() !== Unit.STATUS_RUNNING) {
missing_deps.push(dep_name)
}
}
}
return missing_deps
} catch (e) {
throw error_context(e, {
service,
service_dependencies,
})
}
}
}
module.exports = exports = Analyzer