UNPKG

@rustable/enum

Version:

Rust-inspired pattern matching and type-safe error handling for TypeScript. Includes Option<T> for null-safety and Result<T, E> for error handling, with comprehensive pattern matching support

219 lines (215 loc) 6.71 kB
'use strict'; var utils = require('@rustable/utils'); "use strict"; function variant(target, name, descriptor) { descriptor.value = function(...args) { return new target(name, ...args); }; return descriptor; } exports.Enums = void 0; (function(Enums2) { function create(arg1, arg2) { const Anonymous = class extends Enum { }; if (arg2) { if (typeof arg1 === "string") { Object.defineProperty(Anonymous, "name", { value: arg1, writable: false, configurable: false }); } else { throw new Error("Invalid arguments for create function"); } } else if (typeof arg1 === "string") { throw new Error("Invalid arguments for create function"); } const variants = arg2 || arg1; for (const [variantName] of Object.entries(variants)) { Object.defineProperty(Anonymous, variantName, { value: (...args) => new Anonymous(variantName, ...args), writable: false, configurable: false }); Object.defineProperty(Anonymous.prototype, `is${variantName}`, { value: function() { return this.is(variantName); }, writable: false, configurable: false }); Object.defineProperty(Anonymous.prototype, `let${variantName}`, { value: function(callback) { return this.let(variantName, callback); }, writable: false, configurable: false }); } return Anonymous; } Enums2.create = create; })(exports.Enums || (exports.Enums = {})); class Enum { constructor(name, ...vars) { this.name = name; this.vars = vars; } /** * Checks if the enum is a specific variant * @param variant The variant name to check * @returns true if the enum is the specified variant */ is(variant2) { return this.name === variant2; } /** * Checks if the enum is a specific variant and executes a callback if it matches * @param variant The variant name to check * @param callback The callback function to execute if the variant matches * @returns The result of the callback if variant matches, undefined otherwise */ let(variant2, cb) { return this.is(variant2) ? cb.if(...this.vars || []) : typeof cb.else === "function" ? cb.else() : cb.else; } /** * Unwraps the first argument of a variant * @throws Error if the variant has no arguments * @returns The first argument of the variant */ unwrap() { if (!this.vars || this.vars.length === 0) { throw new Error("Cannot unwrap a variant without arguments"); } return this.vars[0]; } /** * Unwraps all arguments of a variant as a tuple * @throws Error if the variant has no arguments * @returns Tuple of all variant arguments */ unwrapTuple() { if (!this.vars || this.vars.length === 0) { throw new Error("Cannot unwrap a variant without arguments"); } return [...this.vars]; } /** * Converts the enum to a string representation * Format: VariantName for variants without arguments * Format: VariantName(arg1, arg2, ...) for variants with arguments */ toString() { if (!this.vars || this.vars.length === 0) { return this.name; } return `${this.name}(${this.vars.join(", ")})`; } /** * Pattern matches on the enum variant, similar to Rust's enum expression * Use this method to handle different variants of the enum in a type-safe way. * * @param patterns Object mapping variant names to handler functions * @param defaultPatterns Optional default patterns to use if a variant isn't matched * @throws Error if no matching pattern is found and no default pattern is provided * @example * ```typescript * enum.enum({ * Success: (value) => `Got ${value}`, * Error: (err) => `Error: ${err.message}`, * }) * ``` */ match(patterns) { const variantName = this.name; const handler = patterns[variantName] ?? patterns["_"]; if (typeof handler === "undefined") { throw new Error("No handler found."); } if (typeof handler !== "function") { return handler; } const fn = handler; if (!this.vars || this.vars.length === 0) { return fn(); } return fn(...this.vars); } /** * Checks if this enum instance equals another enum instance * Compares both variant names and their arguments */ eq(other) { if (!(other instanceof this.constructor)) { return false; } const { name: thisName, vars: thisArgs } = this; const { name: otherName, vars: otherArgs } = other; if (thisName !== otherName) { return false; } if (!thisArgs && !otherArgs) { return true; } if (!thisArgs || !otherArgs) { return false; } if (thisArgs.length !== otherArgs.length) { return false; } return thisArgs.every((arg, i) => { const otherArg = otherArgs[i]; if (typeof arg === "object" && "eq" in arg) { return arg.eq(otherArg); } return utils.equals(arg, otherArg); }); } /** * Creates a deep clone of the current enum instance * @returns A new instance of the enum with the same variant and cloned arguments */ clone(hash = /* @__PURE__ */ new WeakMap()) { const Constructor = this.constructor; if (!this.vars || this.vars.length === 0) { return new Constructor(this.name); } const clonedArgs = this.vars.map((v) => utils.deepClone(v, hash)); return new Constructor(this.name, ...clonedArgs); } /** * Replaces the current variant with a new one, returning the old variant * @param newVariant The new variant to replace with * @param ...args Arguments for the new variant * @throws Error if the new variant is not a valid variant of this enum * @returns The old variant instance */ replace(newInstance) { if (!(newInstance instanceof this.constructor)) { throw new Error("Invalid instance type"); } const oldVariant = new this.constructor(this.name, ...this.vars || []); this.vars = [...newInstance.vars]; this.name = newInstance.name; return oldVariant; } /** * Modifies the arguments of the current variant based on the variant name * @param patterns Object mapping variant names to modifier functions * @throws Error if no matching pattern is found for the current variant */ modify(patterns) { const variantName = this.name; const modifier = patterns[variantName]; if (!modifier) { return; } if (!this.vars || this.vars.length === 0) { return; } this.vars = modifier(...this.vars); } } exports.Enum = Enum; exports.variant = variant;