88 lines
4.2 KiB
TypeScript
88 lines
4.2 KiB
TypeScript
import type { HwpodOperationResponse } from "@/types";
|
|
|
|
export type WorkspacePreviewMode = "text" | "markdown";
|
|
|
|
export interface BuildOperationPresentation {
|
|
status: string;
|
|
label: string;
|
|
tone: "idle" | "running" | "ok" | "warn" | "error";
|
|
operationId: string | null;
|
|
exitCode: number | null;
|
|
jobId: string | null;
|
|
log: string;
|
|
terminal: boolean;
|
|
}
|
|
|
|
export function workspacePreviewMode(path: string): WorkspacePreviewMode {
|
|
return /\.(?:md|markdown)$/iu.test(path.trim()) ? "markdown" : "text";
|
|
}
|
|
|
|
export function presentBuildOperation(operation: HwpodOperationResponse | null, pending = false): BuildOperationPresentation {
|
|
if (!operation) {
|
|
return {
|
|
status: pending ? "submitting" : "idle",
|
|
label: pending ? "正在提交编译" : "尚未编译",
|
|
tone: pending ? "running" : "idle",
|
|
operationId: null,
|
|
exitCode: null,
|
|
jobId: null,
|
|
log: "",
|
|
terminal: false,
|
|
};
|
|
}
|
|
|
|
const activity = record(operation.result);
|
|
const response = record(activity.response);
|
|
const result = Array.isArray(response.results) ? record(response.results[0]) : {};
|
|
const output = record(result.output);
|
|
const commands = Array.isArray(output.commands) ? output.commands.map(record) : [];
|
|
const lastCommand = commands.at(-1) ?? {};
|
|
const stdout = stringValue(output.stdout) || stringValue(lastCommand.stdout);
|
|
const stderr = stringValue(output.stderr) || stringValue(lastCommand.stderr);
|
|
const parsed = parseJsonRecord(stdout);
|
|
const toolStatus = stringValue(parsed.status).toLowerCase();
|
|
const operationStatus = stringValue(operation.status).toLowerCase() || "unknown";
|
|
const exitCode = numberValue(output.exitCode, lastCommand.exitCode, parsed.return_code, parsed.returnCode);
|
|
const jobId = stringValue(parsed.job_id) || stringValue(parsed.jobId) || null;
|
|
const asyncQueued = parsed.accepted === true && ["queued", "running", "accepted"].includes(toolStatus);
|
|
const failed = operation.error != null
|
|
|| activity.ok === false
|
|
|| response.ok === false
|
|
|| result.ok === false
|
|
|| output.ok === false
|
|
|| (exitCode !== null && exitCode !== 0)
|
|
|| ["failed", "error", "canceled", "cancelled", "timed-out", "timeout"].includes(toolStatus)
|
|
|| ["failed", "canceled", "cancelled", "terminated", "timed-out", "timeout"].includes(operationStatus);
|
|
const terminal = !asyncQueued && ["completed", "succeeded", "failed", "canceled", "cancelled", "terminated", "timed-out", "timeout"].includes(operationStatus);
|
|
const running = pending || asyncQueued || ["accepted", "queued", "running", "pending", "in-progress"].includes(operationStatus);
|
|
const log = [stderr ? `stderr\n${stderr.trim()}` : "", stdout ? `stdout\n${stdout.trim()}` : ""].filter(Boolean).join("\n\n");
|
|
|
|
if (failed) return { status: toolStatus || operationStatus, label: "编译失败", tone: "error", operationId: operation.operationId, exitCode, jobId, log, terminal: true };
|
|
if (asyncQueued) return { status: toolStatus, label: "构建已排队,等待真实终态", tone: "warn", operationId: operation.operationId, exitCode, jobId, log, terminal: false };
|
|
if (running) return { status: operationStatus, label: "正在等待 Windows Keil 构建终态", tone: "running", operationId: operation.operationId, exitCode, jobId, log, terminal: false };
|
|
if (terminal) return { status: operationStatus, label: "编译通过", tone: "ok", operationId: operation.operationId, exitCode, jobId, log, terminal: true };
|
|
return { status: operationStatus, label: "编译状态未知", tone: "warn", operationId: operation.operationId, exitCode, jobId, log, terminal: false };
|
|
}
|
|
|
|
function record(value: unknown): Record<string, unknown> {
|
|
return value && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : {};
|
|
}
|
|
|
|
function stringValue(value: unknown): string {
|
|
return typeof value === "string" ? value.trim() : "";
|
|
}
|
|
|
|
function numberValue(...values: unknown[]): number | null {
|
|
for (const value of values) {
|
|
const number = Number(value);
|
|
if (Number.isFinite(number)) return number;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function parseJsonRecord(value: string): Record<string, unknown> {
|
|
if (!value) return {};
|
|
try { return record(JSON.parse(value)); }
|
|
catch { return {}; }
|
|
}
|