domain/architecture/system/effs/io.bend

near-v-framework@0.5.0.0 · 135 lines · 0 definitions

1import Base
2
3# V's I/O: running a program with its arguments (the effect below), and reading and writing files with
4# Base's File API.
5
6# ---- running a program ----
7# Temporary: bend-kit-process does this, but no Bend version runs both it (2.0.27 only) and bend-kit-files (2.0.28+).
8def Program.run(cmd: String, args: String) -> IO(Result<&1, &1, U32 & String, String>):
9  import "./program_run.c"
10  import "./program_run.js"
11
12def out_of(r: Result<&1, &1, U32 & String, String>) -> String:
13  match r:
14    case Done{out}:
15      out
16    case Fail{_}:
17      ""
18
19def ok_of(r: Result<&1, &1, U32 & String, String>) -> Bool:
20  match r:
21    case Done{_}:
22      True{}
23    case Fail{_}:
24      False{}
25
26# A program's standard output when it exits 0, else "".
27def run(cmd: String, args: List<&2, String>) -> IO(String):
28  do IO<String>:
29    r : Result<&1, &1, U32 & String, String> <- Program.run(cmd, String.join(args, "\u{1f}"))
30    return out_of(r)
31
32# Whether a program exits 0.
33def succeeds(cmd: String, args: List<&2, String>) -> IO(Bool):
34  do IO<Bool>:
35    r : Result<&1, &1, U32 & String, String> <- Program.run(cmd, String.join(args, "\u{1f}"))
36    return ok_of(r)
37
38def home() -> IO(String):
39  do IO<String>:
40    r : Result<&1, &1, U32 & String, String> <- IO.get_env("HOME")
41    return Result.default(&1, &1, U32 & String, String, r, "")
42
43# ---- reading a file: its text, or "" when it cannot be read ----
44def size_of(r: Result<&1, &1, U32 & String, U32>) -> U32:
45  match r:
46    case Done{n}:
47      n
48    case Fail{_}:
49      0
50
51def text_of(r: Result<&1, &1, U32 & String, String>) -> String:
52  match r:
53    case Done{t}:
54      t
55    case Fail{_}:
56      ""
57
58def read_done(r: File & Result<&1, &1, U32 & String, String>) -> IO(String):
59  match r:
60    case (f, got):
61      do IO<String>:
62        File.close(f)
63        return text_of(got)
64
65def read_sized(r: File & Result<&1, &1, U32 & String, U32>) -> IO(String):
66  match r:
67    case (f, n):
68      do IO<String>:
69        got : File & Result<&1, &1, U32 & String, String> <- File.read(f, size_of(n))
70        read_done(got)
71
72def read_opened(r: Result<&1, &1, U32 & String, File>) -> IO(String):
73  match r:
74    case Done{f}:
75      do IO<String>:
76        n : File & Result<&1, &1, U32 & String, U32> <- File.size(f)
77        read_sized(n)
78    case Fail{_}:
79      IO.pure(String, "")
80
81def read_head_opened(r: Result<&1, &1, U32 & String, File>, max: U32) -> IO(String):
82  match r:
83    case Done{f}:
84      do IO<String>:
85        got : File & Result<&1, &1, U32 & String, String> <- File.read(f, max)
86        read_done(got)
87    case Fail{_}:
88      IO.pure(String, "")
89
90# The first `max` bytes of a file ("" if unreadable): enough for what sits at its top, like imports.
91def read_head(path: String, max: U32) -> IO(String):
92  do IO<String>:
93    r : Result<&1, &1, U32 & String, File> <- File.open(path, "r")
94    read_head_opened(r, max)
95
96def read(path: String) -> IO(String):
97  do IO<String>:
98    r : Result<&1, &1, U32 & String, File> <- File.open(path, "r")
99    read_opened(r)
100
101# ---- writing a file ----
102def after_write(r: File & Result<&1, &1, U32 & String, Unit>) -> IO(Unit):
103  match r:
104    case (f, _):
105      File.close(f)
106
107def write_opened(r: Result<&1, &1, U32 & String, File>, text: String) -> IO(Unit):
108  match r:
109    case Done{f}:
110      do IO<Unit>:
111        w : File & Result<&1, &1, U32 & String, Unit> <- File.write(f, text)
112        after_write(w)
113    case Fail{_}:
114      IO.die(Unit, 1, "cannot write a file")
115
116def write(path: String, text: String) -> IO(Unit):
117  do IO<Unit>:
118    r : Result<&1, &1, U32 & String, File> <- File.open(path, "w")
119    write_opened(r, text)
120
121def keep_filled(c: Bool, x: String, rest: List<&2, String>) -> List<&2, String>:
122  match c:
123    case True{}:
124      x <> rest
125    case False{}:
126      rest
127
128# The trimmed, non-empty strings.
129def nonempty(xs: List<&2, String>) -> List<&2, String>:
130  match xs:
131    case []:
132      []
133    case +x <> rest:
134      keep_filled(Bool.not(String.is_empty(String.trim(x))), String.trim(x), nonempty(rest))
135