main.bend
snap@1.1.0.0 · 68 lines · 0 definitions
1# snap: run programs from Bend 2 with argv as a list of strings, no shell.
2#
3# `run` (and `exec`) run a program and answer its exit status on its own first
4# line, then stdout and stderr together; `code`, `text` and `ok` read that
5# answer. `start` leaves a program running and answers its pid. `par` runs
6# several at once. Every argument reaches the program whole; an argv with no
7# program name, or with NUL inside an argument, is refused and nothing runs.
8# `cmd` and `line` join argv on newlines. This file is the interface:
9# everything under src/ is internal.
10import Base
11import snap@1.1.0.0/src/effect.bend as Effect
12import snap@1.1.0.0/src/argv.bend as Argv
13import snap@1.1.0.0/src/answer.bend as Answer
14import snap@1.1.0.0/src/par.bend as Par
15
16# a program run with its arguments: the exit status on its own first line,
17# then stdout and stderr together. A refused argv answers 127 and runs nothing.
18def run(argv: List<&2, String>) -> IO(String): # noqa: L001 IO entry point
19 Argv.run.with(Effect.snaprun.exec, Argv.run.plan(argv))
20
21# a program run with its arguments; the same as `run`
22def exec(argv: List<&2, String>) -> IO(String): # noqa: L001 IO entry point
23 run(argv)
24
25# a program started with its arguments and left running; the answer is its
26# pid, or 0 when it could not be started or its argv was refused
27def start(argv: List<&2, String>) -> IO(String): # noqa: L001 IO entry point
28 Argv.start.with(Effect.snaprun.start, Argv.run.plan(argv))
29
30# several programs run at once, each answered exactly as `run` answers one, in
31# the order the jobs were given; a job `run` would refuse answers 127 in its
32# place. `at` is a directory the caller owns and this fills: job n writes to
33# `at/n`. A width of "" leaves the count to the effect, which reads the cores
34# and divides them by `gb`, the memory cap each job runs under. `by` is the
35# second every job must be finished by, or "0" for none.
36def par( # noqa: L001 IO entry point
37 js: List<&2, List<&2, String>>,
38 +width: String,
39 +gb: String,
40 +at: String,
41 +by: String
42) -> IO(List<&2, String>):
43 do IO<List<&2, String>>:
44 +out : String <- Effect.snaprun.par(Par.par.plan(js, width, gb, at, by))
45 +cs : List<&2, String> <- IO.pure(List<&2, String>, Par.par.nonblank(String.lines(out)))
46 bodies : List<&2, Maybe<&2, String>> <- Par.par.bodies(cs, at, 0n)
47 return Par.par.answers(cs, bodies)
48
49# the exit status an answer holds: everything before its first newline
50def code(out: String) -> String:
51 Answer.code(out)
52
53# what the program printed, stdout and stderr together: everything after the
54# answer's first newline
55def text(out: String) -> String:
56 Answer.text(out)
57
58# whether an answer's status is 0
59def ok(+out: String) -> Bool:
60 Answer.ok(out)
61
62# the arguments joined on newlines, the wire ez's `pass` effect reads
63def cmd(argv: List<&2, String>) -> String:
64 Argv.cmd(argv)
65
66# the arguments joined on newlines; the same as `cmd`
67def line(argv: List<&2, String>) -> String:
68 Argv.line(argv)