62 lines
1.8 KiB
TypeScript
62 lines
1.8 KiB
TypeScript
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 as unknown)) {
|
|
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,
|
|
[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 = <T>(value: T): MatchBuilder<T, never> =>
|
|
buildMatch<T, never>(value, []);
|
|
|
|
export type { Matcher, Pattern };
|
|
export { P };
|