diff --git a/src/commands/config.ts b/src/commands/config.ts index f53b390..ad3471d 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -1,41 +1,39 @@ import { die } from "../fmt.ts" import * as config from "../config.ts" -const KEYS: Record string; validate: (v: string) => string | null }> = { - memory: { - default: "16G", - description: "Container memory limit (e.g. 4G, 16G, 32G, 64G)", - normalize: (v) => v.toUpperCase(), - validate: (v) => /^[1-9]\d*[GMgm]$/.test(v) ? null : "Must be a number followed by G or M (e.g. 16G)", - }, +const VALID_KEYS = ["memory"] as const +type Key = (typeof VALID_KEYS)[number] + +function validateMemory(v: string): string { + if (!/^[1-9]\d*[GMgm]$/.test(v)) die("Must be a number followed by G or M (e.g. 16G)") + return v.toUpperCase() } export async function action(args: string[]) { if (args.length === 0) { const cfg = await config.load() - for (const [key, meta] of Object.entries(KEYS)) { - const val = cfg[key as keyof config.Config] - const display = val ?? `${meta.default} (default)` + for (const key of VALID_KEYS) { + const val = cfg[key] + const display = val ?? `${config.DEFAULTS[key]} (default)` console.log(`${key} = ${display}`) - console.log(` ${meta.description}`) } return } const [key, ...rest] = args - if (!(key in KEYS)) die(`Unknown config key: ${key}\nAvailable keys: ${Object.keys(KEYS).join(", ")}`) + if (!VALID_KEYS.includes(key as Key)) die(`Unknown config key: ${key}\nAvailable keys: ${VALID_KEYS.join(", ")}`) if (rest.length === 0) { - const val = await config.get(key as keyof config.Config) - console.log(val ?? `${KEYS[key].default} (default)`) + const val = await config.get(key as Key) + console.log(val ?? `${config.DEFAULTS[key as Key]} (default)`) return } if (rest.length > 1) die(`Too many arguments. Usage: sandlot config ${key} `) - const meta = KEYS[key] - const error = meta.validate(rest[0]) - if (error) die(error) - const normalized = meta.normalize(rest[0]) - await config.set(key as keyof config.Config, normalized) - console.log(`${key} = ${normalized}`) + + if (key === "memory") { + const normalized = validateMemory(rest[0]) + await config.set("memory", normalized) + console.log(`memory = ${normalized}`) + } } diff --git a/src/config.ts b/src/config.ts index d547247..437b9e9 100644 --- a/src/config.ts +++ b/src/config.ts @@ -4,6 +4,10 @@ import { dirname, join } from "path" const CONFIG_PATH = join(homedir(), ".config", "sandlot", "config.json") +export const DEFAULTS = { + memory: "16G", +} as const + export interface Config { memory?: string } @@ -13,9 +17,7 @@ export async function load(): Promise { if (!(await file.exists())) return {} const raw = await file.json() if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return {} - const cfg: Config = {} - if (typeof raw.memory === "string") cfg.memory = raw.memory - return cfg + return raw as Config } export async function save(config: Config): Promise { diff --git a/src/vm.ts b/src/vm.ts index 3475bcf..b284313 100644 --- a/src/vm.ts +++ b/src/vm.ts @@ -4,7 +4,7 @@ import { homedir } from "os" import { dirname, join } from "path" import { requireApiKey } from "./env.ts" import { info } from "./fmt.ts" -import { get as getConfig } from "./config.ts" +import { get as getConfig, DEFAULTS } from "./config.ts" const DEBUG = !!process.env.DEBUG const CONTAINER_NAME = "sandlot" @@ -67,7 +67,7 @@ function hostMounts(home: string): { dev: boolean; code: boolean } { /** Pull the image and start the container in detached mode. */ async function createContainer(home: string): Promise { const mounts = hostMounts(home) - const memory = (await getConfig("memory")) ?? "16G" + const memory = (await getConfig("memory")) ?? DEFAULTS.memory const args = ["container", "run", "-d", "--name", CONTAINER_NAME, "-m", memory] if (mounts.dev) args.push("--mount", `type=bind,source=${home}/dev,target=/host/dev,readonly`) if (mounts.code) args.push("--mount", `type=bind,source=${home}/code,target=/host/code,readonly`)