Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 18 additions & 7 deletions tools/roms/canonicalize.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, test } from "bun:test";
import { existsSync, mkdtempSync, writeFileSync, readFileSync } from "node:fs";
import { existsSync, mkdirSync, mkdtempSync, writeFileSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";
import {
Expand Down Expand Up @@ -191,23 +191,34 @@ describe("matchAndReport", () => {
describe("main", () => {
test("reports missing datfile value clearly", () => {
const originalStderrWrite = process.stderr.write;
const originalExit = process.exit;
let stderr = "";

process.stderr.write = ((chunk: string | Uint8Array) => {
stderr += String(chunk);
return true;
}) as typeof process.stderr.write;
process.exit = ((code?: number) => {
throw new Error(`exit:${code}`);
}) as typeof process.exit;

try {
expect(() => main(["--datfile", "--dir", "/tmp"])).toThrow("exit:64");
const code = main(["--datfile"]);
expect(code).toBe(64);
expect(stderr).toContain("missing value for --datfile");
} finally {
process.stderr.write = originalStderrWrite;
process.exit = originalExit;
}
});

test("accepts hyphen-prefixed datfile and directory values", () => {
const tmp = mkdtempSync(join(tmpdir(), "rom-cli-hyphen-"));
const originalCwd = process.cwd();
writeFileSync(join(tmp, "-set.dat"), FIXTURE_DATFILE);
mkdirSync(join(tmp, "-roms"));

try {
process.chdir(tmp);
const code = main(["--datfile", "-set.dat", "--dir", "-roms"]);
expect(code).toBe(0);
} finally {
process.chdir(originalCwd);
}
});
});
36 changes: 25 additions & 11 deletions tools/roms/canonicalize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,16 +253,24 @@ interface Args {
readonly apply: boolean;
}

class ArgError extends Error {
readonly exitCode: number;

constructor(message: string, exitCode: number) {
super(message);
this.exitCode = exitCode;
}
}

function parseArgs(argv: readonly string[]): Args {
let datfile: string | undefined;
let dir: string | undefined;
let apply = false;

function readOptionValue(index: number, flag: string): string {
const value = argv[index + 1];
if (value === undefined || value.startsWith("-")) {
process.stderr.write(`missing value for ${flag}\n`);
process.exit(64);
if (value === undefined) {
throw new ArgError(`missing value for ${flag}`, 64);
}
return value;
}
Expand All @@ -284,26 +292,32 @@ function parseArgs(argv: readonly string[]): Args {
" --dir Directory containing ROM files to match.\n" +
" --apply Actually rename files (default: dry-run report).\n",
);
process.exit(0);
throw new ArgError("", 0);
} else {
process.stderr.write(`unknown arg: ${arg}\n`);
process.exit(64);
throw new ArgError(`unknown arg: ${arg}`, 64);
}
}

if (!datfile) {
process.stderr.write("--datfile is required\n");
process.exit(64);
throw new ArgError("--datfile is required", 64);
}
if (!dir) {
process.stderr.write("--dir is required\n");
process.exit(64);
throw new ArgError("--dir is required", 64);
}
return { datfile, dir, apply };
}

export function main(argv: readonly string[]): number {
const args = parseArgs(argv);
let args: Args;
try {
args = parseArgs(argv);
} catch (e) {
if (e instanceof ArgError) {
if (e.message) process.stderr.write(`${e.message}\n`);
return e.exitCode;
}
throw e;
}

if (!existsSync(args.datfile)) {
process.stderr.write(`datfile not found: ${args.datfile}\n`);
Expand Down
Loading