From 0e1f447fdddecba07d5ccab83d555638fbf4750f Mon Sep 17 00:00:00 2001 From: shtse8 Date: Wed, 20 Mar 2024 03:03:24 +0800 Subject: [PATCH] Add switch utility to handle conditional logic --- src/index.ts | 5 ++- src/switch.ts | 73 ++++++++++++++++++++++++++++++++++++++++++++ tests/switch.test.ts | 52 +++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 src/switch.ts create mode 100644 tests/switch.test.ts diff --git a/src/index.ts b/src/index.ts index 8fd3a5b..b29ac0e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,6 +8,7 @@ export * from './array' export * from './semantic' export * from './chain' export * from './object' +export * from './switch' import * as typed from './typed' import * as types from './types' @@ -18,6 +19,7 @@ import * as array from './array' import * as text from './semantic' import * as chain from './chain' import * as object from './object' +import * as switchOps from './switch' export default { ...typed, ...types, @@ -27,5 +29,6 @@ export default { ...array, ...text, ...chain, - ...object + ...object, + ...switchOps } \ No newline at end of file diff --git a/src/switch.ts b/src/switch.ts new file mode 100644 index 0000000..1f58fb9 --- /dev/null +++ b/src/switch.ts @@ -0,0 +1,73 @@ +class InlineSwitch { + private cases = new Map any>(); + private defaultCase?: () => any; + + constructor(private readonly value: T) { } + + // Method to add a case + case(caseValue: T, result: () => U): InlineSwitch { + this.cases.set(caseValue, result); + return this as any; + } + + // Method to set the default case + default(result: () => U): Omit, 'default'> { + if (this.defaultCase) { + throw new Error("Default case already set."); + } + this.defaultCase = result; + return this as any; + } + + // Method to execute the switch + execute(): R | E { + const result = this.cases.get(this.value); + if (result) { + return result(); + } + + if (this.defaultCase) { + return this.defaultCase(); + } + + return undefined as any; + } +} + +/** + * Creates a new InlineSwitch instance for given value. This utility function + * facilitates a fluent interface for conditional logic based on the value provided, + * allowing for a more readable and expressive alternative to traditional switch + * statements or if-else chains. The InlineSwitch class supports adding cases + * with `.case()` method calls and optionally setting a default case with `.default()`. + * The `.execute()` method evaluates the cases against the value and returns the + * result of the matching case or the default case, if provided. + * + * @param value The value to be matched against the defined cases in the InlineSwitch instance. + * @returns A new instance of InlineSwitch configured with the provided value. + * + * @example + * // Using inlineSwitch to determine fruit colors. + * const fruitColor = inlineSwitch('apple') + * .case('apple', () => 'red') + * .case('banana', () => 'yellow') + * .case('orange', () => 'orange') + * .default(() => 'unknown color') + * .execute(); + * + * console.log(fruitColor); // Outputs: 'red' + * + * @example + * // Using inlineSwitch with mixed return types and a default case. + * const processedValue = inlineSwitch('kiwi') + * .case('apple', () => 42) + * .case('banana', () => true) + * .case('orange', () => 'orange') + * .default(() => null) + * .execute(); + * + * console.log(processedValue); // Outputs: null + */ +export function inlineSwitch(value: T) { + return new InlineSwitch(value); +} diff --git a/tests/switch.test.ts b/tests/switch.test.ts new file mode 100644 index 0000000..6af6f76 --- /dev/null +++ b/tests/switch.test.ts @@ -0,0 +1,52 @@ +import { describe, test, it, expect } from 'bun:test' +import { inlineSwitch } from '../src/index'; + +describe('InlineSwitch', () => { + it('should correctly handle a matching case', () => { + const result = inlineSwitch('apple') + .case('apple', () => 'red') + .case('banana', () => 'yellow') + .execute(); + + expect(result).toBe('red'); + }); + + it('should return the default case when no cases match', () => { + const result = inlineSwitch('kiwi') + .case('apple', () => 'red') + .case('banana', () => 'yellow') + .default(() => 'unknown color') + .execute(); + + expect(result).toBe('unknown color'); + }); + + it('should handle mixed return types', () => { + const result = inlineSwitch('banana') + .case('apple', () => 42) + .case('banana', () => true) + .default(() => 'unknown') + .execute(); + + expect(result).toBe(true); + }); + + it('should throw an error if no cases match and no default case is provided', () => { + const result = inlineSwitch('kiwi') + .case('apple', () => 'red') + .case('banana', () => 'yellow') + .execute() + + expect(result).toBeUndefined(); + }); + + it('should prevent setting multiple default cases', () => { + expect(() => { + inlineSwitch('kiwi') + .default(() => 'default 1') + // @ts-ignore + .default(() => 'default 2') + .execute(); + }).toThrow("Default case already set."); + }); +});