// the pull page's url grammar, where goes in raw and `reroute` in // src/hooks.ts folds its segments back together before routing: // // /pulls//latest diff of the newest version // /pulls//1 diff of version 1 // /pulls//1/c1..c2 that diff narrowed to a commit range // /pulls//0..1 interdiff between two versions // /pulls//0..1/ that interdiff narrowed to one change export interface Revspec { base: string; head: string; } // parses `..`, where either side may be missing export const parseRange = (input: string): Revspec | null => { const spec = input.trim(); if (spec === "") return { base: "", head: "" }; // `...` is a symmetric difference in git, and two ranges are not a range if (spec.includes("...") || spec.split("..").length > 2) return null; if (!spec.includes("..")) return { base: "", head: spec }; const [base, head] = spec.split("..", 2).map((part) => part.trim()); if (base === "" && head === "") return null; return { base, head }; }; export interface PullDiffView { mode: "diff"; version: number; base: string; head: string; } export interface PullInterdiffView { mode: "interdiff"; oldVersion: number; version: number; from: Revspec; // oldVersion {base, head} to: Revspec; // version {base, head} // "" means all changes changeId: string; } export type PullView = PullDiffView | PullInterdiffView; // a bad version sends you to the newest one, a bad range only drops itself export type PullRouteRedirect = { redirect: "latest" | "version" }; export const isRedirect = ( result: T | PullRouteRedirect ): result is PullRouteRedirect => "redirect" in result; const index = (raw: string, count: number): number | null => { if (!/^\d+$/.test(raw)) return null; const value = Number(raw); return value < count ? value : null; }; // `/..[/]` export const parseInterdiffRoute = ( versions: readonly Revspec[], versionParam: string, rangeParam?: string ): PullInterdiffView | PullRouteRedirect => { const range = parseRange(versionParam); if (!range) return { redirect: "latest" }; const version1 = index(range.base, versions.length); const version2 = index(range.head, versions.length); if (version1 === null || version2 === null) return { redirect: "latest" }; const changeId = rangeParam === "all" ? "" : (rangeParam ?? ""); return { mode: "interdiff", oldVersion: version1, version: version2, from: versions[version1], to: versions[version2], changeId }; }; // `/[/..]` export const parseDiffRoute = ( versions: readonly Revspec[], versionParam: string, rangeParam?: string ): PullDiffView | PullRouteRedirect => { const version = versionParam === "latest" ? Math.max(versions.length - 1, 0) : index(versionParam, versions.length); if (version === null) return { redirect: "latest" }; const spec = parseRange(rangeParam ?? ""); if (!spec) return { redirect: "version" }; const isDiffBase = spec.base === "" || spec.base === "base"; const isDiffHead = spec.head === "" || spec.head === "head"; return { mode: "diff", version, base: isDiffBase ? (versions[version]?.base ?? "") : spec.base, head: isDiffHead ? (versions[version]?.head ?? "") : spec.head }; };