/// interface RulebookOptions { noun? : T; } /** * Without the Runner, Rulebooks were not "thread-safe", so we need the runner. */ class RulebookRunner { public noun : T; private rulesToExecute : Array = []; public rulebook : Rulebook; public rule : Rule; private ruleRunner : number = -1; public constructor (rulebook : Rulebook, noun : T) { this.rulebook = rulebook; this.noun = noun; } public addRulebooks (...rulebooks : Array>) { rulebooks = arrayUniqueNewArray(rulebooks); rulebooks.forEach((rulebook) => { this.addRules(...rulebook.rules); }); } public addRules (...rules) { if (this.ruleRunner < 0) { this.rulesToExecute.push(...rules); } } public skipRule (rule : Rule) { if (this.ruleRunner >= 0) { let index = this.rulesToExecute.indexOf(rule); if (index > this.ruleRunner) { this.rulesToExecute.splice(index, 1); console.debug("[Rulebook]" + this.rulebook.name + ", skipping Rule " + rule.name + " due to request."); } else { console.warn("[Rulebook]" + this.rulebook.name + ": uname to skip Rule" + rule.name + " due to it being too late to stop it."); } } } public async execute () { arrayUnique(this.rulesToExecute).sort(function (a: Rule, b: Rule) { return a.compareTo(b); }); for (this.ruleRunner = 0; this.ruleRunner < this.rulesToExecute.length; this.ruleRunner++) { this.rule = this.rulesToExecute[this.ruleRunner]; let result = await this.rule.execute(this); if (result != undefined) { return result; } } } } class Rulebook { public rules : Array = []; public name : string; private static indentantionSpaces = 2; public static rulebookStack : Array = []; public static getStack () { let stack = []; Rulebook.rulebookStack.forEach((rl) => { if (rl instanceof Object && rl.name != undefined) { stack.push(rl.name); } else if (typeof rl == "string") { stack.push(rl); } else if (rl instanceof Object && rl.id != undefined) { stack.push(rl.id); } else { stack.push("Undefined"); } }); return stack; } public static getIndentation () { return " ".repeat(Rulebook.indentantionSpaces).repeat(Rulebook.rulebookStack.length); } public static increaseIndentation (rulebook : any) { Rulebook.rulebookStack.push(rulebook); } public static decreaseIndentation () { Rulebook.rulebookStack.pop(); } public static isRunning (r : Rule | Rulebook) { return Rulebook.rulebookStack.indexOf(r) != -1; } public isRunning () { return Rulebook.isRunning(this); } public constructor (name : string) { this.name = name; } public async execute (options : RulebookOptions, ...rulebooks) : Promise{ options = options == undefined ? {} : options; let runner = new RulebookRunner(this, options.noun); // duplicate array runner.addRulebooks(this, ...rulebooks); var names = []; for (let i = 0; i < rulebooks.length; i++) { if (rulebooks[i] === this) continue; names.push(rulebooks[i].name); } console.debug(Rulebook.getIndentation() + "[RULEBOOK] " + this.name + (names.length > 0 ? (" merged with " + names.join(", ")) : "")); Rulebook.increaseIndentation(this); let result = await runner.execute(); Rulebook.decreaseIndentation(); return result; } public createAndAddRule (r : RuleOptions) : Rule { let rule = new Rule(r); this.addRule(rule); return rule; } public addRule (r : Rule) { this.rules.push(r); } public sortRules () { this.rules.sort(function (a : Rule, b : Rule) { return a.compareTo(b); }); } }