import type { FileDiffContentsLoader } from "@pierre/diffs"; import { squashAtomCommandFailure, type AtomCommandResult, } from "@t3tools/client-runtime/state/runtime"; import type { EnvironmentId, PullRequestDiffFileContentsInput, PullRequestDiffFileContentsResult, PullRequestRef, ReviewDiffFileContentsInput, ReviewDiffFileContentsResult, ReviewDiffPreviewSourceKind, } from "@t3tools/contracts"; import { resolveFileDiffPath } from "./diffRendering"; interface GitDiffFileContentsSource { readonly environmentId: EnvironmentId; readonly cwd: string; readonly sourceKind: ReviewDiffPreviewSourceKind; readonly baseRef: string | null; readonly headRef: string | null; /** The comparison identity Pierre carries into its hydrated render cache. */ readonly cacheKey: string; } interface PullRequestDiffFileContentsSource { readonly environmentId: EnvironmentId; readonly reference: PullRequestRef; readonly commit: string | null; readonly cacheKey: string; } type GetDiffFileContents = (request: { readonly environmentId: EnvironmentId; readonly input: ReviewDiffFileContentsInput; }) => Promise>; type GetPullRequestDiffFileContents = (request: { readonly environmentId: EnvironmentId; readonly input: PullRequestDiffFileContentsInput; }) => Promise>; function createDiffFileContentsLoader( load: (input: { readonly changeType: PullRequestDiffFileContentsInput["changeType"]; readonly oldPath: string; readonly newPath: string; }) => Promise<{ readonly oldContents: string; readonly newContents: string }>, cacheKey: string, ): FileDiffContentsLoader { return async (fileDiff) => { const newPath = resolveFileDiffPath(fileDiff); const oldPath = fileDiff.prevName ? resolveFileDiffPath({ ...fileDiff, name: fileDiff.prevName }) : newPath; const contents = await load({ changeType: fileDiff.type, oldPath, newPath }); const newFile = { name: newPath, contents: contents.newContents, cacheKey: `${cacheKey}:new:${newPath}`, }; if (fileDiff.type === "rename-pure") { return { oldFile: null, newFile }; } return { oldFile: { name: oldPath, contents: contents.oldContents, cacheKey: `${cacheKey}:old:${oldPath}`, }, newFile, }; }; } /** Turns the host's Git file-content RPC into the full-file loader Pierre uses for hunk expansion. */ export function createGitDiffFileContentsLoader( getDiffFileContents: GetDiffFileContents, source: GitDiffFileContentsSource, ): FileDiffContentsLoader { return createDiffFileContentsLoader(async ({ changeType, oldPath, newPath }) => { const result = await getDiffFileContents({ environmentId: source.environmentId, input: { cwd: source.cwd, sourceKind: source.sourceKind, changeType, baseRef: source.baseRef, headRef: source.headRef, oldPath, newPath, }, }); if (result._tag !== "Success") { throw squashAtomCommandFailure(result); } return result.value; }, source.cacheKey); } /** Loads host-backed PR files, which may name revisions this checkout has never fetched. */ export function createPullRequestDiffFileContentsLoader( getDiffFileContents: GetPullRequestDiffFileContents, source: PullRequestDiffFileContentsSource, ): FileDiffContentsLoader { return createDiffFileContentsLoader(async ({ changeType, oldPath, newPath }) => { const result = await getDiffFileContents({ environmentId: source.environmentId, input: { ...source.reference, ...(source.commit === null ? {} : { commit: source.commit }), changeType, oldPath, newPath, }, }); if (result._tag !== "Success") { throw squashAtomCommandFailure(result); } return result.value; }, source.cacheKey); }