import { EntityRegistry, IEntityRegistry, } from '@civ-clone/core-registry/EntityRegistry'; import { IConstructor } from '@civ-clone/core-registry/Registry'; import Rule from './Rule'; type RuleArgs = T extends Rule ? C : never; type RuleReturn = T extends Rule ? R : never; export interface IRuleRegistry< T extends Rule = Rule, P extends any[] = any[], R = any > extends IEntityRegistry { get(ruleType: IConstructor): RuleType[]; invalidateCache(rule: T | IConstructor): void; process( ruleType: IConstructor, ...args: RuleArgs ): RuleReturn[]; } interface RuleCacheMap extends Map, Rule[]> { get(ruleType: IConstructor): T[]; set(ruleType: IConstructor, rules: T[]): this; } export class RuleRegistry extends EntityRegistry implements IRuleRegistry { #cache: RuleCacheMap = new Map(); constructor() { super(Rule); } entries(): Rule[] { return super .entries() .sort( (a: Rule, b: Rule): number => a.priority().value() - b.priority().value() ); } get(ruleType: IConstructor): RuleType[] { if (!this.#cache.has(ruleType)) { this.#cache.set( ruleType, this.filter( (rule: Rule): rule is RuleType => rule.enabled() && rule instanceof ruleType ) ); } return this.#cache.get(ruleType) || []; } invalidateCache(rule: Rule | IConstructor): void { this.#cache.delete( rule instanceof Rule ? (rule.constructor as IConstructor) : (rule as IConstructor) ); } process( ruleType: IConstructor, ...args: RuleArgs ): RuleReturn[] { return this.get(ruleType) .filter((rule: RuleType): boolean => rule.validate(...args)) .map((rule: RuleType): RuleReturn => rule.process(...args)); } register(...rules: Rule[]) { super.register(...rules); rules.forEach((rule: Rule): void => this.invalidateCache(rule)); } unregister(...rules: Rule[]): void { super.unregister(...rules); rules.forEach((rule: Rule): void => this.invalidateCache(rule)); } } export const instance: RuleRegistry = new RuleRegistry(); export default RuleRegistry;