Skip to content

Commit

Permalink
dependency maintenance; rewrote build in typescript; added first shor…
Browse files Browse the repository at this point in the history
…t-form run functions
  • Loading branch information
j50n committed Feb 19, 2022
1 parent d5c88a0 commit 1833b7f
Show file tree
Hide file tree
Showing 14 changed files with 196 additions and 58 deletions.
27 changes: 16 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@ behaviors for your data. It also lets you customize `stderr` and error handling.
With just a little code for definition, you can work with bytes or text,
synchronous or asynchronous, buffered or unbuffered.

#### An Example
### An Example

To get you started, here is a simple example where we pass a text `string` to a
To get you started, here is an example where we pass a text `string` to a
process and get back a `Uint8Array` - text compressed to bytes using `gzip`.
This uses the "short-form" function that takes a string as input and returns a
`Uint8array` as output.

```ts
/**
Expand All @@ -48,15 +50,18 @@ process and get back a `Uint8Array` - text compressed to bytes using `gzip`.
* @return The text compressed into bytes.
*/
async function gzip(text: string): Promise<Uint8Array> {
return await runner(stringInput(), bytesOutput())().run({
cmd: ["gzip", "-c"],
}, text);
return await proc.runSB({ cmd: ["gzip", "-c"] }, text);
}

console.dir(await gzip("Hello, world."));
/* prints an array of bytes to console. */
```

> ℹ️ **Short Form** The short form run functions are new. It seems a little odd
> to have so many different typed functions, but it cuts out a lot of
> boilerplate. This feature will most likely be under development for some time.
> See [runner.ts](./runner.ts) for available short form run functions.
## Input Types

| Name | Description |
Expand Down Expand Up @@ -146,12 +151,12 @@ manually).

Most of the time, `proc` can automatically clean up processes. In some cases
where the output of one process feeds into the input of another, the first
process won't be fully processed and therefore cannot be automatically shut
down. This can also happen if you don't fully process `AsyncIterable` output of
a process. This will result in resource leakage. If your program is short and
does not start many processes, or if you are sure that the way you are using
processes is well behaved (either non-streaming output or all output data is
fully consumed), you can use the short form safely.
process's output won't be fully read, and therefore the process cannot be
automatically shut down. This can also happen if you don't fully process
`AsyncIterable` output of a process. This will result in resource leakage. If
your program is short and does not start many processes, or if you are sure that
the way you are using processes is well behaved (either non-streaming output or
all output data is fully consumed), you can use the short form safely.

### Direct Control Over `stderr`

Expand Down
29 changes: 0 additions & 29 deletions build

This file was deleted.

45 changes: 45 additions & 0 deletions build.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env -S deno run --quiet --allow-run

import * as proc from "./mod.ts";
import { dirname } from "./runners/utility.ts";

async function findAllTypescriptFiles(): Promise<string[]> {
return await proc.run0Sa(
{ cmd: ["find", dirname(import.meta), "-name", "*.ts"] },
);
}

async function updateDependencies(): Promise<void> {
await proc.run00({ cmd: ["udd", ...(await findAllTypescriptFiles())] });
}

async function format(): Promise<void> {
await proc.run00({ cmd: ["deno", "fmt", dirname(import.meta)] });

/* Fix breaks to legacy shebang caused by deno formatter. */
await proc.run00(
{
cmd: [
"sed",
"-i",
'2s|^":";\\s[/][/]#;|":" //#;|',
...(await findAllTypescriptFiles()),
],
},
);
}

async function lint(): Promise<void> {
await proc.run00({ cmd: ["deno", "lint", dirname(import.meta)] });
}

async function test(): Promise<void> {
await proc.run00({
cmd: ["deno", "test", "--allow-run", "--reload", dirname(import.meta)],
});
}

await updateDependencies();
await format();
await lint();
await test();
4 changes: 2 additions & 2 deletions deps-test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from "https://deno.land/std@0.125.0/testing/asserts.ts";
export * from "https://deno.land/std@0.126.0/testing/asserts.ts";
export * from "https://deno.land/x/[email protected]/mod.ts";
export * from "https://deno.land/std@0.125.0/fmt/colors.ts";
export * from "https://deno.land/std@0.126.0/fmt/colors.ts";
6 changes: 3 additions & 3 deletions deps.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from "https://deno.land/std@0.125.0/textproto/mod.ts";
export * from "https://deno.land/std@0.125.0/async/mod.ts";
export * from "https://deno.land/std@0.125.0/io/mod.ts";
export * from "https://deno.land/std@0.126.0/textproto/mod.ts";
export * from "https://deno.land/std@0.126.0/async/mod.ts";
export * from "https://deno.land/std@0.126.0/io/mod.ts";
2 changes: 1 addition & 1 deletion examples/pushiterable/example-of-pushiterable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { Answer, Question } from "./common-json-defs.ts";
import * as proc from "../../mod.ts";
import { asynciter } from "https://deno.land/x/[email protected]/mod.ts";
import { blue, red } from "https://deno.land/std@0.125.0/fmt/colors.ts";
import { blue, red } from "https://deno.land/std@0.126.0/fmt/colors.ts";

/**
* This demonstrates sending objects to and receiving objects from a child process
Expand Down
35 changes: 35 additions & 0 deletions run-examples.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env -S deno run --quiet --allow-run

import * as proc from "./mod.ts";
import { dirname } from "./runners/utility.ts";

const here = dirname(import.meta);

proc.run00({
cmd: [
"bash",
"--login",
"-c",
`
set -e
cd "${here}/examples/warandpeace" && (
set -x
time ./countwords.sh < ./warandpeace.txt.gz
time ./countwords.ts < ./warandpeace.txt.gz
time ./countwords2.ts < ./warandpeace.txt.gz
echo "a b c d" | ./countwords2.ts || true
time ./countwords3.ts < ./warandpeace.txt.gz
echo "a b c d" | ./countwords3.ts || true
)
cd "${here}/examples/pushiterable" && (
PATH=".:$PATH" ./example-of-pushiterable.ts
)
cd "${here}/examples/sounds" && (
./sounds.ts
)
`,
],
});
66 changes: 61 additions & 5 deletions runner.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { RunnerImpl } from "./runner-impl.ts";
import { bytesInput, bytesOutput } from "./runners/handlers/bytes.ts";
import { emptyInput, emptyOutput } from "./runners/handlers/empty.ts";
import { stringInput, stringOutput } from "./runners/handlers/string.ts";
import { stringArrayOutput } from "./runners/handlers/string-array.ts";
import {
Group,
group,
Expand Down Expand Up @@ -40,10 +43,63 @@ export function runner<A, B>(
}

/**
* A simple runner. `stdin` is empty. `stdout` and `stderr` are directed to the parent.
* Note that `stdout` is treated is assumed to be text and not byte data.
* @returns A process runner.
* A simple runner.
* - `stdin` is empty.
* - `stdout` and `stderr` are redirected to the parent.
* - the global group is used.
*/
export async function run00(options: RunOptions): Promise<void> {
await new RunnerImpl(globalGroup, emptyInput(), emptyOutput()).run(
options,
);
}

/**
* A non-streaming runner for `string[]` output.
* - `stdout` is interpreted as lines of text and returned as a `string[]`.
* - `stdin` is empty.
* - `stderr` is redirected to the parent.
* - the global group is used.
*/
export async function run0Sa(options: RunOptions): Promise<string[]> {
return await new RunnerImpl(globalGroup, emptyInput(), stringArrayOutput())
.run(
options,
);
}

/**
* A non-streaming runner for `Uint8Array` input and `string` output.
* - `stdout` is interpreted as a `string`.
* - `stdin` is interpreted as a `Uint8Array`.
* - `stderr` is redirected to the parent.
* - the global group is used.
*/
export async function runBS(
options: RunOptions,
input: Uint8Array,
): Promise<string> {
return await new RunnerImpl(globalGroup, bytesInput(), stringOutput())
.run(
options,
input,
);
}

/**
* A non-streaming runner for `string`input and `Uint8Array` output.
* - `stdout` is interpreted as a `Uint8Array`.
* - `stdin` is interpreted as a `string`.
* - `stderr` is redirected to the parent.
* - the global group is used.
*/
export function simpleRunner(group?: Group): Runner<void, void> {
return new RunnerImpl(group || globalGroup, emptyInput(), emptyOutput());
export async function runSB(
options: RunOptions,
input: string,
): Promise<Uint8Array> {
return await new RunnerImpl(globalGroup, stringInput(), bytesOutput())
.run(
options,
input,
);
}
2 changes: 1 addition & 1 deletion runners/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isWindows } from "https://deno.land/std@0.125.0/_util/os.ts";
import { isWindows } from "https://deno.land/std@0.126.0/_util/os.ts";

export const LINESEP: string = (() => {
if (isWindows) {
Expand Down
2 changes: 1 addition & 1 deletion runners/handlers/bytes.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { assertEquals } from "https://deno.land/std@0.125.0/testing/asserts.ts";
import { assertEquals } from "https://deno.land/std@0.126.0/testing/asserts.ts";
import * as proc from "../../mod.ts";

Deno.test({
Expand Down
8 changes: 4 additions & 4 deletions runners/handlers/empty-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
import { ErrorHandler } from "../error-support.ts";
import { InputHandler } from "../proc-group.ts";
import { StderrProcessor } from "../stderr-support.ts";
import { AbstractTextUnbufferedOutputHandler } from "./abstract-handlers.ts";
import { AbstractBytesUnbufferedOutputHandler } from "./abstract-handlers.ts";

/**
* Empty `stdin`.
Expand All @@ -26,7 +26,7 @@ export class EmptyInputHandler implements InputHandler<void> {
* Write lines of `stdout` to `stdout` of the parent process, unbuffered.
*/
export class EmptyOutputHandler
extends AbstractTextUnbufferedOutputHandler<void> {
extends AbstractBytesUnbufferedOutputHandler<void> {
constructor(
processStderr: StderrProcessor,
errorHandler: ErrorHandler,
Expand All @@ -40,8 +40,8 @@ export class EmptyOutputHandler
process: MultiCloseProcess,
input: { stdin: MultiCloseWriter; handlerResult: Promise<null | Error> },
): Promise<void> {
for await (const line of this.process(stdout, stderr, process, input)) {
console.log(line);
for await (const bytes of this.process(stdout, stderr, process, input)) {
Deno.stdout.writeSync(bytes);
}
}
}
2 changes: 1 addition & 1 deletion runners/handlers/empty.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Deno.test(
async function say(text: string): Promise<void> {
const pg = proc.group();
try {
await proc.simpleRunner(pg).run({
await proc.run00({
cmd: [
"spd-say",
"-w",
Expand Down
9 changes: 9 additions & 0 deletions runners/utility.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { BufReader, BufWriter } from "../deps.ts";
import * as path from "https://deno.land/[email protected]/path/mod.ts";

export const DEFAULT_BUFFER_SIZE = 4096;

Expand Down Expand Up @@ -207,3 +208,11 @@ export async function sleep(delayms: number): Promise<void> {
setTimeout(() => resolve(), delayms)
);
}

export function filename(meta: { url: string }): string {
return path.fromFileUrl(meta.url);
}

export function dirname(meta: { url: string }): string {
return path.dirname(filename(meta));
}
17 changes: 17 additions & 0 deletions tests/proc.readme.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
stringInput,
stringOutput,
} from "../mod.ts";
import * as proc from "../mod.ts";

Deno.test({
name: "[README] Key Concepts | Leaking Resources",
Expand Down Expand Up @@ -54,6 +55,22 @@ Deno.test({
},
});

Deno.test({
name: "[README] Input and Output Types (Short Form)",
async fn() {
/**
* Use `gzip` to compress some text.
* @param text The text to compress.
* @return The text compressed into bytes.
*/
async function gzip(text: string): Promise<Uint8Array> {
return await proc.runSB({ cmd: ["gzip", "-c"] }, text);
}

console.dir(await gzip("Hello, world."));
},
});

Deno.test({
name: "[README] Examples | Run an Inline Bash Script",
async fn() {
Expand Down

0 comments on commit 1833b7f

Please sign in to comment.