119 lines
3.7 KiB
JavaScript
119 lines
3.7 KiB
JavaScript
|
/**
|
|||
|
* @callback Handler
|
|||
|
* Handle a value, with a certain ID field set to a certain value.
|
|||
|
* The ID field is passed to `zwitch`, and it’s value is this function’s
|
|||
|
* place on the `handlers` record.
|
|||
|
* @param {...any} parameters
|
|||
|
* Arbitrary parameters passed to the zwitch.
|
|||
|
* The first will be an object with a certain ID field set to a certain value.
|
|||
|
* @returns {any}
|
|||
|
* Anything!
|
|||
|
*/
|
|||
|
|
|||
|
/**
|
|||
|
* @callback UnknownHandler
|
|||
|
* Handle values that do have a certain ID field, but it’s set to a value
|
|||
|
* that is not listed in the `handlers` record.
|
|||
|
* @param {unknown} value
|
|||
|
* An object with a certain ID field set to an unknown value.
|
|||
|
* @param {...any} rest
|
|||
|
* Arbitrary parameters passed to the zwitch.
|
|||
|
* @returns {any}
|
|||
|
* Anything!
|
|||
|
*/
|
|||
|
|
|||
|
/**
|
|||
|
* @callback InvalidHandler
|
|||
|
* Handle values that do not have a certain ID field.
|
|||
|
* @param {unknown} value
|
|||
|
* Any unknown value.
|
|||
|
* @param {...any} rest
|
|||
|
* Arbitrary parameters passed to the zwitch.
|
|||
|
* @returns {void|null|undefined|never}
|
|||
|
* This should crash or return nothing.
|
|||
|
*/
|
|||
|
|
|||
|
/**
|
|||
|
* @template {InvalidHandler} [Invalid=InvalidHandler]
|
|||
|
* @template {UnknownHandler} [Unknown=UnknownHandler]
|
|||
|
* @template {Record<string, Handler>} [Handlers=Record<string, Handler>]
|
|||
|
* @typedef Options
|
|||
|
* Configuration (required).
|
|||
|
* @property {Invalid} [invalid]
|
|||
|
* Handler to use for invalid values.
|
|||
|
* @property {Unknown} [unknown]
|
|||
|
* Handler to use for unknown values.
|
|||
|
* @property {Handlers} [handlers]
|
|||
|
* Handlers to use.
|
|||
|
*/
|
|||
|
|
|||
|
const own = {}.hasOwnProperty
|
|||
|
|
|||
|
/**
|
|||
|
* Handle values based on a field.
|
|||
|
*
|
|||
|
* @template {InvalidHandler} [Invalid=InvalidHandler]
|
|||
|
* @template {UnknownHandler} [Unknown=UnknownHandler]
|
|||
|
* @template {Record<string, Handler>} [Handlers=Record<string, Handler>]
|
|||
|
* @param {string} key
|
|||
|
* Field to switch on.
|
|||
|
* @param {Options<Invalid, Unknown, Handlers>} [options]
|
|||
|
* Configuration (required).
|
|||
|
* @returns {{unknown: Unknown, invalid: Invalid, handlers: Handlers, (...parameters: Parameters<Handlers[keyof Handlers]>): ReturnType<Handlers[keyof Handlers]>, (...parameters: Parameters<Unknown>): ReturnType<Unknown>}}
|
|||
|
*/
|
|||
|
export function zwitch(key, options) {
|
|||
|
const settings = options || {}
|
|||
|
|
|||
|
/**
|
|||
|
* Handle one value.
|
|||
|
*
|
|||
|
* Based on the bound `key`, a respective handler will be called.
|
|||
|
* If `value` is not an object, or doesn’t have a `key` property, the special
|
|||
|
* “invalid” handler will be called.
|
|||
|
* If `value` has an unknown `key`, the special “unknown” handler will be
|
|||
|
* called.
|
|||
|
*
|
|||
|
* All arguments, and the context object, are passed through to the handler,
|
|||
|
* and it’s result is returned.
|
|||
|
*
|
|||
|
* @this {unknown}
|
|||
|
* Any context object.
|
|||
|
* @param {unknown} [value]
|
|||
|
* Any value.
|
|||
|
* @param {...unknown} parameters
|
|||
|
* Arbitrary parameters passed to the zwitch.
|
|||
|
* @property {Handler} invalid
|
|||
|
* Handle for values that do not have a certain ID field.
|
|||
|
* @property {Handler} unknown
|
|||
|
* Handle values that do have a certain ID field, but it’s set to a value
|
|||
|
* that is not listed in the `handlers` record.
|
|||
|
* @property {Handlers} handlers
|
|||
|
* Record of handlers.
|
|||
|
* @returns {unknown}
|
|||
|
* Anything.
|
|||
|
*/
|
|||
|
function one(value, ...parameters) {
|
|||
|
/** @type {Handler|undefined} */
|
|||
|
let fn = one.invalid
|
|||
|
const handlers = one.handlers
|
|||
|
|
|||
|
if (value && own.call(value, key)) {
|
|||
|
// @ts-expect-error Indexable.
|
|||
|
const id = String(value[key])
|
|||
|
// @ts-expect-error Indexable.
|
|||
|
fn = own.call(handlers, id) ? handlers[id] : one.unknown
|
|||
|
}
|
|||
|
|
|||
|
if (fn) {
|
|||
|
return fn.call(this, value, ...parameters)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
one.handlers = settings.handlers || {}
|
|||
|
one.invalid = settings.invalid
|
|||
|
one.unknown = settings.unknown
|
|||
|
|
|||
|
// @ts-expect-error: matches!
|
|||
|
return one
|
|||
|
}
|