Skip to content

Commit

Permalink
imp: Implemented CallableObject & SwitchableCallback.
Browse files Browse the repository at this point in the history
  • Loading branch information
Byloth committed Nov 28, 2024
1 parent de8323b commit f2c6121
Show file tree
Hide file tree
Showing 13 changed files with 309 additions and 176 deletions.
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@byloth/core",
"version": "2.0.0-rc.7",
"version": "2.0.0-rc.8",
"description": "An unopinionated collection of useful functions and classes that I use widely in all my projects. 🔧",
"keywords": [
"Core",
Expand Down Expand Up @@ -58,10 +58,10 @@
},
"devDependencies": {
"@byloth/eslint-config-typescript": "^3.0.3",
"@types/node": "^22.9.0",
"@types/node": "^22.10.1",
"husky": "^9.1.7",
"typescript": "^5.6.3",
"typescript": "^5.7.2",
"vite": "^5.4.11"
},
"packageManager": "pnpm@9.3.0+sha512.ee7b93e0c2bd11409c6424f92b866f31d3ea1bef5fbe47d3c7500cdc3c9668833d2e55681ad66df5b640c61fa9dc25d546efa54d76d7f8bf54b13614ac293631"
"packageManager": "pnpm@9.14.3+sha512.c0f53ee99477ed969b82b289ad011a5d16bf1623c957e7f29eabe8d0c00b574c29b8c7f54f6c67ee710c73f285c8154d07ce44b46fe2c0eeb476a90441bac371"
}
312 changes: 156 additions & 156 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const VERSION = "2.0.0-rc.7";
export const VERSION = "2.0.0-rc.8";

export type { Constructor, Interval, Timeout } from "./core/types.js";

Expand All @@ -7,6 +7,7 @@ export { isBrowser, isNode, isWebWorker } from "./helpers.js";
export {
AggregatedIterator,
AggregatedAsyncIterator,
CallableObject,
Clock,
Countdown,
DeferredPromise,
Expand All @@ -30,6 +31,7 @@ export {
SmartIterator,
SmartAsyncIterator,
SmartPromise,
SwitchableCallback,
Thenable,
TimeoutException,
TimedPromise,
Expand All @@ -41,6 +43,7 @@ export {
export type {
AsyncGeneratorFunction,
AsyncIteratorLike,
Callback,
FulfilledHandler,
GeneratorFunction,
Iteratee,
Expand All @@ -66,7 +69,6 @@ export type {
PromiseResolver,
Reducer,
RejectedHandler,
Subscriber,
TypeGuardIteratee

} from "./models/types.js";
Expand Down
23 changes: 23 additions & 0 deletions src/models/callbacks/callable-object.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

import type { Callback } from "./types.js";

export const SmartFunction = (Function as unknown) as
new<T extends Callback<any[], any> = () => void>(...args: string[]) =>
(...args: Parameters<T>) => ReturnType<T>;

export default abstract class CallableObject<T extends Callback<any[], any> = () => void>
extends SmartFunction<T>
{
public constructor()
{
super(`return this.invoke(...arguments);`);

const self = this.bind(this);
Object.setPrototypeOf(this, self);

return self as this;
}

public abstract invoke(...args: Parameters<T>): ReturnType<T>;
}
5 changes: 5 additions & 0 deletions src/models/callbacks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import CallableObject, { SmartFunction } from "./callable-object.js";
import Publisher from "./publisher.js";
import SwitchableCallback from "./switchable-callback.js";

export { CallableObject, Publisher, SmartFunction, SwitchableCallback };
11 changes: 5 additions & 6 deletions src/models/publisher.ts → src/models/callbacks/publisher.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import { ReferenceException } from "./exceptions/index.js";
import { ReferenceException } from "../exceptions/index.js";

export type Subscriber<A extends unknown[] = [], R = void> = (...args: A) => R;
import type { Callback } from "./types.js";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export default class Publisher<T extends { [K in keyof T]: Subscriber<any[], any> } = Record<string, Subscriber>>
export default class Publisher<T extends { [K in keyof T]: Callback<any[], any> } = Record<string, Callback>>
{
protected _subscribers: Map<keyof T, Subscriber<unknown[], unknown>[]>;
protected _subscribers: Map<keyof T, Callback<unknown[], unknown>[]>;

public constructor()
{
this._subscribers = new Map();
}

public subscribe<K extends keyof T, S extends T[K]>(event: K, subscriber: S)
: () => void
public subscribe<K extends keyof T, S extends T[K]>(event: K, subscriber: S): () => void
{
if (!(this._subscribers.has(event))) { this._subscribers.set(event, []); }

Expand Down
104 changes: 104 additions & 0 deletions src/models/callbacks/switchable-callback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { KeyException, NotImplementedException, RuntimeException } from "../exceptions/index.js";

import CallableObject from "./callable-object.js";
import type { Callback } from "./types.js";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export default class SwitchableCallback<T extends Callback<any[], any> = Callback> extends CallableObject<T>
{
protected _callback: T;
protected _callbacks: Map<string, T>;

protected _isEnabled: boolean;
public get isEnabled(): boolean { return this._isEnabled; }

protected _key: string;
public get key(): string { return this._key; }

public readonly invoke: (...args: Parameters<T>) => ReturnType<T>;

public constructor()
{
const _default = () =>
{
throw new NotImplementedException(
"The `SwitchableCallback` has no callback defined yet. " +
"Did you forget to call the `register` method?"
);
};

super();

this._callback = ((_default) as unknown) as T;
this._callbacks = new Map<string, T>();

this._isEnabled = false;
this._key = "";

this.invoke = (...args: Parameters<T>): ReturnType<T> => this._callback(...args);
}

public enable(): void
{
if (!(this._key))
{
throw new KeyException(
"The `SwitchableCallback` has no callback defined yet. " +
"Did you forget to call the `register` method?"
);
}
if (this._isEnabled)
{
throw new RuntimeException("The `SwitchableCallback` is already enabled.");
}

this._callback = this._callbacks.get(this._key)!;
this._isEnabled = true;
}
public disable(): void
{
if (!(this._isEnabled))
{
throw new RuntimeException("The `SwitchableCallback` is already disabled.");
}

// eslint-disable-next-line @typescript-eslint/no-empty-function
this._callback = (() => { }) as T;
this._isEnabled = false;
}

public register(key: string, callback: T): void
{
if (this._callbacks.size === 0)
{
this._key = key;
this._callback = callback;
}
else if (this._callbacks.has(key))
{
throw new KeyException(`The key '${key}' has already been used for another callback.`);
}

this._callbacks.set(key, callback);
}
public unregister(key: string): void
{
if (!(this._callbacks.has(key)))
{
throw new KeyException(`The key '${key}' doesn't yet have any associated callback.`);
}

this._callbacks.delete(key);
}

public switch(key: string): void
{
if (!(this._callbacks.has(key)))
{
throw new KeyException(`The key '${key}' doesn't yet have any associated callback.`);
}

this._key = key;
this._callback = this._callbacks.get(key)!;
}
}
1 change: 1 addition & 0 deletions src/models/callbacks/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type Callback<A extends unknown[] = [], R = void> = (...args: A) => R;
5 changes: 2 additions & 3 deletions src/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export {

} from "./aggregators/index.js";

export { CallableObject, Publisher, SmartFunction, SwitchableCallback } from "./callbacks/index.js";
export {
Exception,
FatalErrorException,
Expand All @@ -30,8 +31,6 @@ export { SmartIterator, SmartAsyncIterator } from "./iterators/index.js";
export { JSONStorage } from "./json/index.js";
export { DeferredPromise, LongRunningTask, SmartPromise, Thenable, TimedPromise } from "./promises/index.js";

import Publisher from "./publisher.js";

export { Clock, Countdown } from "./timers/index.js";

export { GameLoop, Publisher };
export { GameLoop };
3 changes: 2 additions & 1 deletion src/models/promises/long-running-task.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { yieldToEventLoop } from "../../utils/async.js";

import Publisher from "../callbacks/publisher.js";
import { RuntimeException } from "../exceptions/index.js";
import Publisher from "../publisher.js";

import type { MaybeAsyncGeneratorFunction } from "../iterators/types.js";
import type { FulfilledHandler, PromiseExecutor, RejectedHandler } from "./types.js";
Expand Down
2 changes: 1 addition & 1 deletion src/models/timers/clock.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { TimeUnit } from "../../utils/date.js";
import { RangeException, RuntimeException } from "../exceptions/index.js";

import Publisher from "../callbacks/publisher.js";
import GameLoop from "../game-loop.js";
import Publisher from "../publisher.js";

interface ClockEventMap
{
Expand Down
3 changes: 1 addition & 2 deletions src/models/timers/countdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { TimeUnit } from "../../utils/date.js";
import { FatalErrorException, RangeException, RuntimeException } from "../exceptions/index.js";
import { DeferredPromise, SmartPromise } from "../promises/index.js";

import Publisher from "../callbacks/publisher.js";
import GameLoop from "../game-loop.js";
import Publisher from "../publisher.js";

interface CountdownEventMap
{
Expand All @@ -17,7 +17,6 @@ interface CountdownEventMap
export default class Countdown extends GameLoop
{
protected _deferrer?: DeferredPromise<void>;

protected _publisher: Publisher<CountdownEventMap>;

protected _duration: number;
Expand Down
2 changes: 1 addition & 1 deletion src/models/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@ export type {

} from "./promises/types.js";

export type { Subscriber } from "./publisher.js";
export type { Callback } from "./callbacks/types.js";

0 comments on commit f2c6121

Please sign in to comment.