libflitter/app/Analyzer.js

/**
 * @module libflitter/app/Analyzer
 */

const Unit = require('../Unit')
const UnitStaticDependencyError = require('../errors/UnitStaticDependencyError')

/**
 * 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
                }
            }

            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

        // 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
    }
}

module.exports = exports = Analyzer