diff --git a/src/cli/output.ts b/src/cli/output.ts new file mode 100644 index 0000000..dfc672e --- /dev/null +++ b/src/cli/output.ts @@ -0,0 +1,20 @@ +import { CliError } from "./errors.ts"; + +export function formatError(error: CliError): string { + const parts: string[] = [`错误: ${error.message}`]; + + if (error.usage) { + parts.push(`用法: ${error.usage}`); + } + + if (error.hint) { + parts.push(`提示: ${error.hint}`); + } + + return parts.join("\n\n"); +} + +export function printError(error: CliError): never { + process.stderr.write(formatError(error) + "\n"); + process.exit(1); +} diff --git a/tests/cli/output.test.ts b/tests/cli/output.test.ts new file mode 100644 index 0000000..717d7b3 --- /dev/null +++ b/tests/cli/output.test.ts @@ -0,0 +1,49 @@ +import { describe, it, expect } from "bun:test"; +import { formatError } from "../../src/cli/output.ts"; +import { + UsageError, + ConfigError, + CommandError, + InternalError, +} from "../../src/cli/errors.ts"; + +describe("formatError", () => { + it("只输出错误行(无 hint/usage)", () => { + const err = new ConfigError("未初始化"); + const output = formatError(err); + expect(output).toBe("错误: 未初始化"); + }); + + it("输出错误行 + 提示行", () => { + const err = new ConfigError("未初始化", { hint: "请先运行 rune init" }); + const output = formatError(err); + expect(output).toBe("错误: 未初始化\n\n提示: 请先运行 rune init"); + }); + + it("输出错误行 + 用法行", () => { + const err = new UsageError("缺少参数", { + usage: "rune plan ", + }); + const output = formatError(err); + expect(output).toBe( + "错误: 缺少参数\n\n用法: rune plan ", + ); + }); + + it("输出完整格式(错误 + 用法 + 提示)", () => { + const err = new UsageError("缺少必填参数 ", { + usage: "rune plan ", + hint: "请指定变更名称,如 \"add-login\"", + }); + const output = formatError(err); + expect(output).toBe( + '错误: 缺少必填参数 \n\n用法: rune plan \n\n提示: 请指定变更名称,如 "add-login"', + ); + }); + + it("InternalError 输出固定消息", () => { + const err = new InternalError(); + const output = formatError(err); + expect(output).toBe("错误: 发生了未预期的错误"); + }); +});