🔥 Remove the match and pattern example modules
They were the template's demo API, not the library's real surface. Drop them, their README walkthrough, and the tooling note that cited their disable directives, and point index.ts at the primitive matchers that remain.
This commit is contained in:
1 parent
ce3d618757
commit
a3eb6183af
5 files changed
+14
-315
No files matched your search
+6
-2
@@ -1,2 +1,6 @@
|
||||
export { match, P } from "./match.ts";
|
||||
export type { Matcher, Pattern } from "./pattern.ts";
|
||||
export {
|
||||
getPrimitiveUnionMatcher,
|
||||
getPrimitiveUnionMatcherPartial,
|
||||
getPrimitiveUnionMatcherPartialW,
|
||||
getPrimitiveUnionMatcherW,
|
||||
} from "./primitive.ts";
|
||||
@@ -1,62 +0,0 @@
|
||||
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 };
|
||||
-105
@@ -1,105 +0,0 @@
|
||||
/**
|
||||
* Pattern matching primitives. Each constructor returns a lightweight
|
||||
* matcher object whose `matches` method returns a type guard.
|
||||
*/
|
||||
|
||||
export interface Matcher<T> {
|
||||
readonly matches: (value: unknown) => value is T;
|
||||
}
|
||||
|
||||
const literalMatcher = <
|
||||
const L extends string | number | boolean | null | undefined,
|
||||
>(
|
||||
value: L,
|
||||
): Matcher<L> => ({
|
||||
matches: (candidate): candidate is L => candidate === value,
|
||||
});
|
||||
const typeMatcher = <T>(
|
||||
type:
|
||||
| "string"
|
||||
| "number"
|
||||
| "boolean"
|
||||
| "bigint"
|
||||
| "symbol"
|
||||
| "undefined"
|
||||
| "object"
|
||||
| "function",
|
||||
): Matcher<T> => {
|
||||
const matches = (value: unknown): value is T => {
|
||||
if (type === "undefined") {
|
||||
return value === undefined;
|
||||
}
|
||||
if (type === "object") {
|
||||
return (
|
||||
(typeof value === "object" && value !== null) ||
|
||||
typeof value === "function"
|
||||
);
|
||||
}
|
||||
return typeof value === type;
|
||||
};
|
||||
return { matches };
|
||||
},
|
||||
whenMatcher = <T>(
|
||||
predicate: (value: unknown) => value is T,
|
||||
): Matcher<T> => ({
|
||||
matches: predicate,
|
||||
}),
|
||||
whenMatcherAny = <T>(
|
||||
predicate: (value: unknown) => boolean,
|
||||
): Matcher<T> => ({
|
||||
matches: (value: unknown): value is T => predicate(value),
|
||||
}),
|
||||
isNestedMatcher = (expected: unknown): expected is Matcher<unknown> =>
|
||||
typeof expected === "object" &&
|
||||
expected !== null &&
|
||||
"matches" in expected,
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters
|
||||
keysMatch = <S extends object>(shape: S, candidate: object): boolean => {
|
||||
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
||||
for (const key of Object.keys(shape) as (keyof S)[]) {
|
||||
if (!(key in candidate)) {
|
||||
return false;
|
||||
}
|
||||
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
||||
const expected = shape[key],
|
||||
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
||||
actual = candidate[key as keyof object];
|
||||
if (isNestedMatcher(expected)) {
|
||||
if (!expected.matches(actual)) {
|
||||
return false;
|
||||
}
|
||||
} else if (actual !== expected) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
structuralMatcher = <S extends object, T extends S>(
|
||||
shape: S,
|
||||
refine?: (value: S) => value is T,
|
||||
): Matcher<T> => ({
|
||||
matches: (value: unknown): value is T => {
|
||||
if (typeof value !== "object" || value === null) {
|
||||
return false;
|
||||
}
|
||||
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
||||
const candidate = value as S;
|
||||
if (!keysMatch(shape, candidate)) {
|
||||
return false;
|
||||
}
|
||||
if (refine && !refine(candidate)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
export const P = {
|
||||
literal: literalMatcher,
|
||||
type: typeMatcher,
|
||||
when: whenMatcher,
|
||||
any: whenMatcherAny,
|
||||
shape: structuralMatcher,
|
||||
} as const;
|
||||
|
||||
export type Pattern<T> = Matcher<T>;
|
||||
Reference in new issue
Block a user