A simple command-line tool to start NetBSD virtual machines using QEMU with sensible defaults.
1import { Table } from "@cliffy/table";
2import dayjs from "dayjs";
3import relativeTime from "dayjs/plugin/relativeTime.js";
4import utc from "dayjs/plugin/utc.js";
5import { Effect, pipe } from "effect";
6import type { Volume } from "../db.ts";
7import type { DbError } from "../mod.ts";
8import { deleteVolume, getVolume, listVolumes } from "../volumes.ts";
9
10dayjs.extend(relativeTime);
11dayjs.extend(utc);
12
13const createTable = () =>
14 Effect.succeed(
15 new Table(
16 ["NAME", "VOLUME ID", "CREATED"],
17 ),
18 );
19
20const populateTable = (table: Table, volumes: Volume[]) =>
21 Effect.sync(() => {
22 for (const volume of volumes) {
23 table.push([
24 volume.name,
25 volume.id,
26 dayjs.utc(volume.createdAt).local().fromNow(),
27 ]);
28 }
29 return table;
30 });
31
32const displayTable = (table: Table) =>
33 Effect.sync(() => {
34 console.log(table.padding(2).toString());
35 });
36
37const handleError = (error: DbError | Error) =>
38 Effect.sync(() => {
39 console.error(`Failed to fetch volumes: ${error}`);
40 Deno.exit(1);
41 });
42
43const lsEffect = () =>
44 pipe(
45 Effect.all([listVolumes(), createTable()]),
46 Effect.flatMap(([volumes, table]) => populateTable(table, volumes)),
47 Effect.flatMap(displayTable),
48 Effect.catchAll(handleError),
49 );
50
51export async function list() {
52 await Effect.runPromise(lsEffect());
53}
54
55export async function remove(name: string) {
56 await Effect.runPromise(
57 pipe(
58 getVolume(name),
59 Effect.flatMap((volume) =>
60 volume
61 ? deleteVolume(volume.id)
62 : Effect.fail(new Error(`Volume with name or ID ${name} not found.`))
63 ),
64 Effect.tap(() =>
65 Effect.sync(() => {
66 console.log(`Volume ${name} deleted successfully.`);
67 })
68 ),
69 Effect.catchAll((error) =>
70 Effect.sync(() => {
71 console.error(`An error occurred: ${error}`);
72 Deno.exit(1);
73 })
74 ),
75 ),
76 );
77}
78
79export async function inspect(name: string) {
80 await Effect.runPromise(
81 pipe(
82 getVolume(name),
83 Effect.flatMap((volume) =>
84 volume
85 ? Effect.sync(() => {
86 console.log(volume);
87 })
88 : Effect.fail(new Error(`Volume with name or ID ${name} not found.`))
89 ),
90 Effect.catchAll((error) =>
91 Effect.sync(() => {
92 console.error(`An error occurred: ${error}`);
93 Deno.exit(1);
94 })
95 ),
96 ),
97 );
98}