All files match.ts

96.77% Statements 60/62
91.66% Branches 11/12
100% Functions 6/6
96.77% Lines 60/62

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 631x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 9x 2x 3x 1x 1x 3x 1x 9x 9x 9x 9x 5x 5x 5x 5x 5x 5x 5x 5x 5x 9x 9x 2x 2x 1x 1x 1x 1x 1x 9x 9x 1x 1x     1x 1x 9x 9x 9x 9x 1x 1x 1x 1x 1x 1x 1x  
import { P, type Matcher, type Pattern } from "./pattern.ts";
 
type Cases<R> = readonly (readonly [Matcher<unknown>, (value: unknown) => R])[];
 
interface MatchBuilder<T, R> {
    with<U extends T, V>(
        pattern: Matcher<U>,
        handler: (value: U) => V,
    ): MatchBuilder<T, R | V>;
    exhaustive(): R;
    otherwise(handler: (value: T) => R): R;
}
 
const buildMatch = <T, R>(value: T, cases: Cases<R>): MatchBuilder<T, R> => {
    const apply = (): R | undefined => {
        for (const [matcher, handler] of cases) {
            if (matcher.matches(value)) {
                return handler(value);
            }
        }
        return undefined;
    };
 
    const builder = {
        with<U extends T, V>(
            pattern: Matcher<U>,
            handler: (value: U) => V,
        ): MatchBuilder<T, R | V> {
            const nextCases: Cases<R | V> = [
                ...cases,
                // oxlint-disable-next-line typescript/no-unsafe-type-assertion
                [pattern, handler as (value: unknown) => R | V],
            ];
            return buildMatch(value, nextCases);
        },
        exhaustive(): R {
            const result = apply();
            if (result === undefined) {
                throw new Error(
                    "tiny-pattern-ts: match.exhaustive() called with no matching case",
                );
            }
            return result;
        },
        otherwise(handler: (value: T) => R): R {
            for (const [matcher, run] of cases) {
                if (matcher.matches(value)) {
                    return run(value);
                }
            }
            return handler(value);
        },
    };
 
    return builder;
};
 
export const match = <T>(value: T): MatchBuilder<T, never> =>
    buildMatch<T, never>(value, []);
 
export type { Matcher, Pattern };
export { P };