|
|
import { ILifetimeSlot } from "../typings/interfaces";
|
|
|
import { isDestroyable } from "./traits";
|
|
|
|
|
|
const _destroy = (item: unknown) => () => isDestroyable(item) && item.destroy();
|
|
|
|
|
|
enum State {
|
|
|
New,
|
|
|
|
|
|
Initialized,
|
|
|
|
|
|
Destroyed
|
|
|
}
|
|
|
|
|
|
export class LifetimeSlot<T> implements ILifetimeSlot<T> {
|
|
|
private _value: NonNullable<T> | undefined = undefined;
|
|
|
|
|
|
private _state = State.New;
|
|
|
|
|
|
cleanup: () => void;
|
|
|
|
|
|
readonly remove: () => void;
|
|
|
|
|
|
constructor(remove: () => void) {
|
|
|
this.remove = this._finalizer(remove);
|
|
|
this.cleanup = this._finalizer(() => { });
|
|
|
}
|
|
|
|
|
|
has(): boolean {
|
|
|
this._assertNotDisposed();
|
|
|
return this._value !== undefined;
|
|
|
}
|
|
|
get(): NonNullable<T> {
|
|
|
this._assertNotDisposed();
|
|
|
if (this._value === undefined)
|
|
|
throw new Error("The slot doesn't have a value");
|
|
|
return this._value;
|
|
|
}
|
|
|
|
|
|
initialize(): boolean {
|
|
|
return this._state === State.New ?
|
|
|
(this._state = State.Initialized, true) :
|
|
|
false;
|
|
|
}
|
|
|
|
|
|
store(item: NonNullable<T>, cleanup: ((item: NonNullable<T>) => void) | undefined = _destroy): void {
|
|
|
this._assertNotDisposed();
|
|
|
this._value = item;
|
|
|
if (cleanup)
|
|
|
this.cleanup = this._finalizer(() => cleanup(item));
|
|
|
}
|
|
|
|
|
|
private _finalizer(cb: () => void) {
|
|
|
return () => this._state !== State.Destroyed ? (this._state = State.Destroyed, cb()) : void (0);
|
|
|
}
|
|
|
|
|
|
private _assertNotDisposed() {
|
|
|
if (this._state === State.Destroyed)
|
|
|
throw new Error("The slot is disposed");
|
|
|
}
|
|
|
|
|
|
}
|