// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. import { File } from "./files.ts"; import { close } from "./ops/resources.ts"; import { ReadCloser, WriteCloser } 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 { args: 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?: WriteCloser; readonly stdout?: ReadCloser; readonly stderr?: ReadCloser; // @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); } } async status(): Promise { return await 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({ args, cwd = undefined, env = {}, stdout = "inherit", stderr = "inherit", stdin = "inherit" }: RunOptions): Process { const res = runOp({ args: args.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); }