Skip to content

Commit

Permalink
add cli
Browse files Browse the repository at this point in the history
  • Loading branch information
spalberg committed Dec 2, 2024
1 parent 1979556 commit 67f4d8d
Show file tree
Hide file tree
Showing 8 changed files with 268 additions and 5 deletions.
87 changes: 87 additions & 0 deletions cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { colors } from "@cliffy/ansi/colors";
import { Command } from "@cliffy/command";
import { Input, Select } from "@cliffy/prompt";
import { readLinesFromFile } from "utils";
import { days, getSolution } from "./days/mod.ts";
import denoJson from "./deno.json" with { type: "json" };
import {
readLinesFromStdin,
readLinesFromUrl,
} from "./utils/readLinesFromStream.ts";

const list = new Command()
.description("List all available solutions")
.action(() => {
console.log("Available solutions:");
for (const day of Object.keys(days)) {
console.log(`- ${day}`);
}
});

const main = new Command()
.name("Advent of Code - 2024")
.version(denoJson.version)
.description("...")
.option("-d, --day <day:string>", "Day to run")
.option("-i, --input <input:file>", "Input file, local path or remote URL")
.action(async (options) => {
let input: Array<string> | null = null;
let stdinClosed = false;
if (options.input != null) {
input = await loadInput(options.input);
} else {
input = await readLinesFromStdin();
stdinClosed = input != null;
input = input ?? await provideInput().then(loadInput);
}
if (input === null) {
console.error("No input provided");
Deno.exit(1);
}
if (stdinClosed && options.day == null) {
console.error("Day not provided");
Deno.exit(1);
}
const day = options.day ?? await selectDay();
const solution = getSolution(day);
if (solution == null) {
console.error(`Day ${day} not found`);
Deno.exit(1);
}
console.log(colors.bold.yellow(`Day ${day}`));
console.log(`Part 1: ${solution.part1(input)}`);
console.log(`Part 2: ${solution.part2(input)}`);
});

async function provideInput(): Promise<string> {
return await Input.prompt({
message: "Provide input (path or URL)",
files: true,
});
}

async function selectDay(): Promise<string> {
return await Select.prompt({
message: "Select day",
options: Object.keys(days).map((day) => ({
name: day.toString().padStart(2, "0"),
value: day,
})),
});
}

async function loadInput(input: string) {
return isWebUrl(input)
? await readLinesFromUrl(input)
: await readLinesFromFile(input);
}

function isWebUrl(path: string): boolean {
return path.startsWith("http://") || path.startsWith("https://");
}

if (import.meta.main) {
await main
.command("list", list)
.parse(Deno.args);
}
18 changes: 18 additions & 0 deletions days/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
type PartFunction = (input: Array<string>) => number;
type Solution = { part1: PartFunction; part2: PartFunction };

const days: Record<string, Solution> = {};

for (const day of [1]) {
days[pad(day)] = await import(`./${pad(day)}/main.ts`);
}

export function getSolution(day: string | number): Solution | null {
return days[pad(day)];
}

export { days };

function pad(day: string | number) {
return day.toString().padStart(2, "0");
}
12 changes: 11 additions & 1 deletion deno.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
{
"tasks": {},
"name": "@aoc/2024",
"version": "0.0.1-rc.1",
"license": "MIT",
"exports": "./cli.ts",
"tasks": {
"cli": "deno run --allow-read ./cli.ts"
},
"imports": {
"@cliffy/ansi": "jsr:@cliffy/[email protected]",
"@cliffy/command": "jsr:@cliffy/[email protected]",
"@cliffy/prompt": "jsr:@cliffy/[email protected]",
"@std/expect": "jsr:@std/expect@1",
"@std/collections": "jsr:@std/collections@1",
"@std/path": "jsr:@std/path@1",
"@std/streams": "jsr:@std/streams@^1.0.8",
"utils": "./utils/mod.ts"
}
}
117 changes: 115 additions & 2 deletions deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions utils/getLines.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { TextLineStream } from "@std/streams";

export async function getLines(
input: ReadableStream<Uint8Array>,
): Promise<Array<string>> {
const stream = input
.pipeThrough(new TextDecoderStream())
.pipeThrough(new TextLineStream({ allowCR: true }));
return await Array.fromAsync(stream);
}
5 changes: 3 additions & 2 deletions utils/loadInput.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { dirname, fromFileUrl, resolve } from "@std/path";
import { readLinesFromFile } from "./readLinesFromStream.ts";

export function loadInput(day: number): Array<string> {
export function loadInput(day: number): Promise<Array<string>> {
const path = resolve(
dirname(fromFileUrl(import.meta.url)),
"..",
"inputs",
`${day.toString().padStart(2, "0")}.txt`,
);
return Deno.readTextFileSync(path).split("\n");
return readLinesFromFile(path);
}
5 changes: 5 additions & 0 deletions utils/mod.ts
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
export { loadInput } from "./loadInput.ts";
export {
readLinesFromFile,
readLinesFromStdin,
readLinesFromUrl,
} from "./readLinesFromStream.ts";
19 changes: 19 additions & 0 deletions utils/readLinesFromStream.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { getLines } from "./getLines.ts";

export async function readLinesFromFile(path: string): Promise<Array<string>> {
using file = await Deno.open(path);
return await getLines(file.readable);
}

export async function readLinesFromStdin(): Promise<Array<string> | null> {
if (Deno.stdin.isTerminal()) return null;
return await getLines(Deno.stdin.readable);
}

export async function readLinesFromUrl(url: string): Promise<Array<string>> {
const response = await fetch(url);
if (!response.ok || response.body === null) {
throw new Error(`Failed to fetch ${url}`);
}
return await getLines(response.body);
}

0 comments on commit 67f4d8d

Please sign in to comment.