Source: modules/utils/Enums.js

/**
 * @module modules/utils/Enums.js
 * @name Enums
 * @copyright 2023 3Liz
 * @author DHONT René-Luc
 * @license MPL-2.0
 */

/**
 * A factory function that accepts a plain enum object and returns a proxied enum object.
 * The enum proxy intercepts the read and write operations on an enum object and:
 * Throws an error when a non-existent enum value is accessed
 * Throws an error when an enum object property is changed
 * @function
 * @example
 * const Sizes = createEnum({
 *      Small: 'small',
 *      Medium: 'medium',
 *      Large: 'large',
 *  })
 * @example
 * const Bool = createEnum({
 *      'Yes': true,
 *      'No': false,
 *      'Undefined': undefined,
 *  });
 * @param {object} structure - The enum structure
 * @throws {TypeError} Will throws an error if the structure is not an object and the values
 * associated to the structure keys are not 'number', 'string', 'boolean' or 'undefined'.
 * @returns {Proxy} The enum based on the structure
 */
const createEnum = (structure) => {
    if (structure === null || typeof structure !== 'object' || Array.isArray(structure)) {
        throw new TypeError(`'${structure}' is not a valid enum structure.`);
    }

    for (const key in structure) {
        if (!['number', 'string', 'boolean', 'undefined'].includes(typeof structure[key])) {
            throw new TypeError(
                `You are only allowed to use 'number', 'string', 'boolean' or 'undefined' types, but you are using '${JSON.stringify(
                    structure[key]
                )}'`
            );
        }
    }

    return new Proxy(structure, {
        set(target, prop) {
            if (Reflect.has(target, prop)) {
                throw new TypeError(`Cannot assign to read only property '${prop}' of an Enum.`);
            } else {
                throw new TypeError(`Property '${prop}' does not exist in the Enum.`);
            }
        },
        get(target, prop) {
            return Reflect.get(target, prop);
        },
    });
};

export { createEnum };