Skip to content

Commit

Permalink
Add switch utility to handle conditional logic
Browse files Browse the repository at this point in the history
  • Loading branch information
shtse8 committed Mar 19, 2024
1 parent eccdad9 commit 0e1f447
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 1 deletion.
5 changes: 4 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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,
Expand All @@ -27,5 +29,6 @@ export default {
...array,
...text,
...chain,
...object
...object,
...switchOps
}
73 changes: 73 additions & 0 deletions src/switch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
class InlineSwitch<T, R = never, E = undefined> {
private cases = new Map<T, () => any>();
private defaultCase?: () => any;

constructor(private readonly value: T) { }

// Method to add a case
case<U>(caseValue: T, result: () => U): InlineSwitch<T, R | U> {
this.cases.set(caseValue, result);
return this as any;
}

// Method to set the default case
default<U>(result: () => U): Omit<InlineSwitch<T, R | U, never>, '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<T>(value: T) {
return new InlineSwitch(value);
}
52 changes: 52 additions & 0 deletions tests/switch.test.ts
Original file line number Diff line number Diff line change
@@ -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.");
});
});

0 comments on commit 0e1f447

Please sign in to comment.