import { P, type Matcher, type Pattern } from "./pattern.ts"; type Cases = readonly (readonly [Matcher, (value: unknown) => R])[]; interface MatchBuilder { with( pattern: Matcher, handler: (value: U) => V, ): MatchBuilder; exhaustive(): R; otherwise(handler: (value: T) => R): R; } const buildMatch = (value: T, cases: Cases): MatchBuilder => { const apply = (): R | undefined => { for (const [matcher, handler] of cases) { if (matcher.matches(value as unknown)) { return handler(value); } } return undefined; }; const builder = { with( pattern: Matcher, handler: (value: U) => V, ): MatchBuilder { const nextCases: Cases = [ ...cases, [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 as unknown)) { return run(value) as R; } } return handler(value); }, }; return builder; }; export const match = (value: T): MatchBuilder => buildMatch(value, []); export type { Matcher, Pattern }; export { P };