import { path } from "./deps.ts";import { abort, debug, stat } from "./utils.ts";
export type EnvValue = boolean | string | Array<string>;type EnvValues = { [name: string]: EnvValue };type EnvFunction = (name?: string, value?: EnvValue) => any;
export class Env { private values: EnvValues;
private constructor() { this.values = { ...{ "--tasks": [], "--debug": !!Deno.env.get("DRAKE_DEBUG"), "--default-task": "", "--cache": "", "--directory": Deno.cwd(), "--abort-exits": false, "--always-make": false, "--dry-run": false, "--help": false, "--list-all": false, "--list-tasks": false, "--quiet": false, "--verbose": false, "--version": false, }, } as EnvValues; }
private setValue(name: string, value: EnvValue) { switch (name) { case "--abort-exits": case "--always-make": case "--debug": case "--dry-run": case "--help": case "--list-all": case "--list-tasks": case "--quiet": case "--verbose": case "--version": if (typeof value !== "boolean") { abort(`${name} must be a boolean`); } break; case "--cache": if (typeof value !== "string") { abort(`${name} must be a string`); } if (value !== "") { const dir = path.dirname(value); if (!stat(dir)?.isDirectory) { abort(`--cache file directory missing or not a directory: ${dir}`); } value = path.join(Deno.realPathSync(dir), path.basename(value)); } break; case "--directory": if (typeof value !== "string") { abort(`${name} must be a string`); } if (!stat(value)?.isDirectory) { abort(`--directory missing or not a directory: ${value}`); } value = Deno.realPathSync(value); Deno.chdir(value); break; case "--default-task": if (typeof value !== "string") { abort(`${name} must be a string`); } break; case "--tasks": if ( !(value instanceof Array) || !value.every((v) => typeof v === "string") ) { abort("--tasks must be a string array"); } break; default: if (name.startsWith("-")) { abort(`illegal option: ${name}`); } if (!/^[a-zA-Z]\w*$/.test(name)) { abort(`illegal variable name: ${name}`); } if (typeof value !== "string") { abort(`variable value must be a string: ${name}`); } } this.values[name] = value; }
static create(): EnvFunction { const env = new Env(); return function ( this: Env, name?: string, value?: EnvValue, ): any { if (name === undefined) { return this; } if (arguments.length !== 1) { debug("set", `${name}: ${value}`); this.setValue(name, value!); } return this.values[name]; }.bind(env); }
parseArgs(args: string[]): void { let arg: string | undefined; while (!!(arg = args.shift())) { const match = arg.match(/^([a-zA-Z]\w*)=(.*)$/); if (match) { this.values[match[1]] = match[2]; continue; } const shortOpts: { [arg: string]: string } = { "-a": "--always-make", "-D": "--debug", "-d": "--directory", "-n": "--dry-run", "-h": "--help", "-l": "--list-tasks", "-L": "--list-all", "-q": "--quiet", "-v": "--verbose", } as const; if (shortOpts[arg] !== undefined) { arg = shortOpts[arg]; } switch (arg) { case "--always-make": case "--debug": case "--dry-run": case "--help": case "--list-tasks": case "--list-all": case "--quiet": case "--version": case "--verbose": this.setValue(arg, true); break; case "--cache": case "--directory": const value = args.shift(); if (value === undefined) { abort(`missing ${arg} option value`); } this.setValue(arg, value); break; default: if (arg.startsWith("-")) { abort(`illegal option: ${arg}`); } (this.values["--tasks"] as Array<string>).push(arg); break; } } }}
export const env = Env.create();