import {InternalLog, logger, BASE_ROOT} from './internal/log';
import {Log} from './log';
import {joinFqn, Supplier} from './util';
export type Constructor<T> = new (...args: any[]) => T;
export type Named<T> = string | { name: string } | Constructor<T>;
let app: Providers;
export class Providers {
private providers: Record<string, any> = {};
private _log: InternalLog | Log | undefined;
get log(): InternalLog | Log | undefined {
if (this._log) {
this._log = logger('Providers');
}
return this._log;
}
table(): void {
this.log?.table(Object.entries(this.providers));
}
reset(): void {
this.log?.debug('Resetting all entries!');
this.providers = {};
}
static reset(): void {
Providers.get().reset();
}
static get(): Providers {
if (!app) {
app = new Providers();
}
return app;
}
register<T extends Record<string, any>>(instance: T, name?: Named<T>): T {
let key: string;
Iif (typeof name === 'string') {
key = name;
}
else if (name) {
key = name.name;
}
else {
key = instance.constructor.name;
}
this.providers[key] = instance;
if (key === Log.name && instance instanceof Log) {
this._log = instance.copy(joinFqn('Providers', BASE_ROOT));
}
this.log?.debug('Registered %s as %s', key, instance.constructor.name);
return instance;
}
static register<T, I extends T>(instance: I, name?: Named<T>): T {
return Providers.get().register(instance, name);
}
provide<T>(providerName: Named<T>, orRegister?: Supplier<T>): T {
let key;
Iif (typeof providerName === 'string') {
key = providerName;
}
else Eif (providerName) {
key = providerName.name;
}
let result = this.providers[key];
this.log?.debug('Initial lookup for %s was %s', key, result?.constructor.name);
if (!result && orRegister) {
result = orRegister();
Eif (result) {
this.log?.debug('Fallback lookup for %s was %s', key, result?.constructor.name);
this.register(result, providerName);
}
else {
throw new Error(`Provider ${key} does not exist!`);
}
}
return result;
}
static provide<T>(providerName: Named<T>, orRegister?: Supplier<T>): T {
return Providers.get().provide(providerName, orRegister);
}
/**
* Prefer registered instance but fallback to using `fallback` without registering.
* @param providerName
* @param fallback
* @see useOrProvide
*/
static provideOrUse<T>(fallback: T, providerName: Named<T>): T {
return Providers.provide(providerName) || fallback;
}
/**
* Prefer `preferred` but fallback to registered `providerName`.
* @param providerName
* @param preferred
* @see provideOrUse
*/
static useOrProvide<T>(providerName: Named<T>, preferred?: T): T {
return preferred || Providers.provide(providerName);
}
}
|