/**
* @description Base class for managing trigger states.
* @copyright 2023-2023
* @author Mike Price <dev.grumptech@gmail.com>
* @module TriggerStateBaseModule
* @requires debug
* @see {@link https://github.com/debug-js/debug#readme}
* @requires is-it-check
* @see {@link https://github.com/evdama/is-it-check}
*/
import _debugModule from 'debug';
import _is from 'is-it-check';
import {TRIGGER_STATES} from './triggerTypes.mjs';
import {TimeTrigger} from './timeTrigger.mjs';
/**
* @description Debugging function pointer for runtime related diagnostics.
* @private
*/
const _debug = _debugModule('time_trigger_state');
/**
* @description Enumeration of the trigger actoions
* @private
* @readonly
* @enum {string}
* @property {string} Next- Perform next action
* @property {string} Abort - Perform abort action
*/
export const TRIGGER_ACTIONS = {
Next : 'next',
Abort : 'abort',
};
/**
* @description Base class for managing trigger states.
*/
export class TriggerStateBase {
/**
* @description Constructor
* @param {object} config - Configuration data
* @param {TimeTrigger} config.owner - Owning Trigger
* @throws {TypeError} - Thrown if 'config' is invalid.
* @throws {Error} - Thrown if instantiating the base class.
* @class
* @private
*/
constructor(config) {
// Validate arguments
if (_is.undefined(config) ||
_is.not.object(config) ||
_is.undefined(config.owner) ||
!(config.owner instanceof TimeTrigger)) {
throw new TypeError(`Invalid configuration.`);
}
// Ensure we are not attempting to instanciate the base class.
if (this.constructor === TriggerStateBase) {
// Prevent creation of the base object.
throw new Error(`Instantiating base class.`);
}
this._owner = config.owner;
}
/**
* @description Read-only property for the name of the state.
* @returns {string} - name
* @throws {Error} - Thrown when calling the base class
* @private
*/
get Name() {
throw new Error(`Abstract Property: Name`);
}
/**
* @description Read-only property for the id of the state.
* @returns {TRIGGER_STATES} - state identifier
* @throws {Error} - Thrown when calling the base class
* @private
*/
get State() {
throw new Error(`Abstract Property: State`);
}
/**
* @description Read-only property for state ids for valid transitions.
* @returns {TRIGGER_STATES[]} - array of state ids
* @throws {Error} - Thrown when calling the base class
* @private
*/
get ValidTransitionStates() {
throw new Error(`Abstract Property: ValidTransitionStates`);
}
/**
* @description Evaluate the trigger action
* @param {TRIGGER_ACTIONS} action - Action to evaluate for the current state.
* @returns {boolean} - true if handled.
* @throws {TypeError} - Thrown if 'action' is invalid.
* @private
*/
Evaluate(action) {
// Validate arguments.
if (_is.not.string(action) ||
_is.under(action.length, 1) ||
(Object.values(TRIGGER_ACTIONS).indexOf(action) < 0)) {
throw new TypeError(`'action' is invalid. ${action}`);
}
let handled = false;
switch (action) {
case TRIGGER_ACTIONS.Next: {
handled = this._doNext();
}
// eslint-disable-next-line indent
break;
case TRIGGER_ACTIONS.Abort: {
handled = this._doAbort();
}
// eslint-disable-next-line indent
break;
default: {
handled = false;
}
// eslint-disable-next-line indent
break;
}
_debug(`State Eval: ${this.Name} evaluating action:${action} result:${handled}`);
return handled;
}
/**
* @description Perform actions when entering this state.
* @param {TriggerStateBase} oldState - State being transitioned from.
* @returns {void}
* @throws {TypeError} thrown if 'oldState' is not a TriggerStateBase object.
* @private
*/
OnEntrance(oldState) {
if (!(oldState instanceof TriggerStateBase)) {
throw new TypeError(`${this.Name}::OnEntrance(). oldState is invalid.`);
}
_debug(`${this.Name}::OnEntrance() called. Transitioning from state ${oldState.Name}`);
}
/**
* @description Perform actions when exiting this state.
* @param {TriggerStateBase} newState - State being transitioned to.
* @returns {void}
* @throws {TypeError} thrown if 'oldState' is not a TriggerStateBase object.
* @private
*/
OnExit(newState) {
if (!(newState instanceof TriggerStateBase)) {
throw new TypeError(`${this.Name}::OnEntrance(). newState is invalid.`);
}
// Validate that we are transitioning to a valid state.
if (!this.ValidTransitionStates.includes(newState.State)) {
// Invalid transition.
throw new Error(`Invalid transition from ${this.Name} to ${newState.Name}`);
}
_debug(`${this.Name}::OnExit() called. Transitioning to state ${newState.Name}`);
}
/**
* @description Perform actions for the transition to the next state.
* @returns {boolean} - true if handled
* @private
*/
_doNext() {
_debug(`${this.Name}::_doNext() called.`);
return false;
}
/**
* @description Perform actions for the abort the current state.
* @returns {boolean} - true if handled
* @private
*/
_doAbort() {
_debug(`${this.Name}::_doAbort() called.`);
return false;
}
}