// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. import { File } from "./files.ts"; import { close } from "./ops/resources.ts"; import { Closer, Reader, Writer } from "./io.ts"; import { readAll } from "./buffer.ts"; import { kill, runStatus as runStatusOp, run as runOp } from "./ops/process.ts"; export type ProcessStdio = "inherit" | "piped" | "null"; // TODO Maybe extend VSCode's 'CommandOptions'? // See https://code.visualstudio.com/docs/editor/tasks-appendix#_schema-for-tasksjson export interface RunOptions { cmd: string[]; cwd?: string; env?: { [key: string]: string }; stdout?: ProcessStdio | number; stderr?: ProcessStdio | number; stdin?: ProcessStdio | number; } async function runStatus(rid: number): Promise { const res = await runStatusOp(rid); if (res.gotSignal) { const signal = res.exitSignal; return { signal, success: false }; } else { const code = res.exitCode; return { code, success: code === 0 }; } } export class Process { readonly rid: number; readonly pid: number; readonly stdin?: Writer & Closer; readonly stdout?: Reader & Closer; readonly stderr?: Reader & Closer; // @internal constructor(res: RunResponse) { this.rid = res.rid; this.pid = res.pid; if (res.stdinRid && res.stdinRid > 0) { this.stdin = new File(res.stdinRid); } if (res.stdoutRid && res.stdoutRid > 0) { this.stdout = new File(res.stdoutRid); } if (res.stderrRid && res.stderrRid > 0) { this.stderr = new File(res.stderrRid); } } status(): Promise { return runStatus(this.rid); } async output(): Promise { if (!this.stdout) { throw new Error("Process.output: stdout is undefined"); } try { return await readAll(this.stdout); } finally { this.stdout.close(); } } async stderrOutput(): Promise { if (!this.stderr) { throw new Error("Process.stderrOutput: stderr is undefined"); } try { return await readAll(this.stderr); } finally { this.stderr.close(); } } close(): void { close(this.rid); } kill(signo: number): void { kill(this.pid, signo); } } export interface ProcessStatus { success: boolean; code?: number; signal?: number; // TODO: Make this a string, e.g. 'SIGTERM'. } function isRid(arg: unknown): arg is number { return !isNaN(arg as number); } interface RunResponse { rid: number; pid: number; stdinRid: number | null; stdoutRid: number | null; stderrRid: number | null; } export function run({ cmd, cwd = undefined, env = {}, stdout = "inherit", stderr = "inherit", stdin = "inherit", }: RunOptions): Process { const res = runOp({ cmd: cmd.map(String), cwd, env: Object.entries(env), stdin: isRid(stdin) ? "" : stdin, stdout: isRid(stdout) ? "" : stdout, stderr: isRid(stderr) ? "" : stderr, stdinRid: isRid(stdin) ? stdin : 0, stdoutRid: isRid(stdout) ? stdout : 0, stderrRid: isRid(stderr) ? stderr : 0, }) as RunResponse; return new Process(res); }