nomen-lang 0.0.19 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/NOMEN_AGENTS.md +57 -12
  2. package/core/System/Array.nm +11 -11
  3. package/core/System/Buffer.nm +2 -2
  4. package/core/System/Channel.nm +146 -12
  5. package/core/System/ClassBuffer.nm +2 -2
  6. package/core/System/Console.nm +28 -17
  7. package/core/System/Controls/Button.nm +96 -8
  8. package/core/System/Controls/CheckBox.nm +117 -12
  9. package/core/System/Controls/Container.nm +114 -8
  10. package/core/System/Controls/Text.nm +98 -8
  11. package/core/System/Controls/TextBox.nm +120 -10
  12. package/core/System/Controls/Window.nm +231 -23
  13. package/core/System/Graph.nm +1 -1
  14. package/core/System/Init.nm +4 -8
  15. package/core/System/LinkedList.nm +1 -1
  16. package/core/System/List.nm +3 -3
  17. package/core/System/Map.nm +5 -5
  18. package/core/System/Math.nm +10 -32
  19. package/core/System/Result.nm +1 -1
  20. package/core/System/Set.nm +4 -4
  21. package/core/System/Stream/Directory.nm +189 -63
  22. package/core/System/Stream/DirectoryError.nm +21 -0
  23. package/core/System/Stream/File.nm +339 -141
  24. package/core/System/Stream/FileError.nm +21 -0
  25. package/core/System/Stream/Http.nm +109 -44
  26. package/core/System/Stream/HttpError.nm +20 -0
  27. package/core/System/String.nm +83 -69
  28. package/core/System/StringBuilder.nm +11 -13
  29. package/core/System/Task.nm +46 -12
  30. package/core/System/Test.nm +1 -1
  31. package/core/System/Text/Json.nm +5 -3
  32. package/core/System/Text/JsonTree.nm +23 -14
  33. package/core/System/Text/Regex.nm +124 -64
  34. package/core/System/Tree.nm +1 -1
  35. package/core/System/bool.nm +4 -9
  36. package/core/System/char.nm +3 -9
  37. package/core/System/float.nm +2 -1
  38. package/core/System/float32.nm +44 -0
  39. package/core/System/float64.nm +44 -0
  40. package/core/System/int.nm +3 -9
  41. package/core/System/int16.nm +47 -0
  42. package/core/System/int32.nm +47 -0
  43. package/core/System/int64.nm +3 -9
  44. package/core/System/int8.nm +3 -9
  45. package/core/System/ufloat.nm +44 -0
  46. package/core/System/ufloat32.nm +44 -0
  47. package/core/System/ufloat64.nm +44 -0
  48. package/core/System/uint.nm +3 -9
  49. package/core/System/uint16.nm +47 -0
  50. package/core/System/uint32.nm +47 -0
  51. package/core/System/uint64.nm +3 -9
  52. package/core/System/uint8.nm +3 -9
  53. package/core/docs/System/Channel.md +2 -0
  54. package/core/docs/System/Result.md +5 -1
  55. package/core/docs/System/Task.md +1 -1
  56. package/dist/index.mjs +38794 -21299
  57. package/package.json +38 -34
  58. package/src/args.ts +11 -0
  59. package/src/index.ts +21 -3
  60. package/src/init.ts +1 -1
  61. package/src/test.ts +48 -4
  62. package/src/types/Config.ts +2 -0
@@ -5,16 +5,27 @@
5
5
  * automatically by its `#destroy` when it goes out of scope. Read methods set
6
6
  * the `eof` flag when the end of the file is reached.
7
7
  *
8
+ * Every operation that can fail returns a `Result` whose error side is a
9
+ * `FileError`; a handle whose `open` failed (or that was closed) reports
10
+ * `FileError.not_open` from subsequent operations instead of crashing.
11
+ *
8
12
  * var File w = File()
9
- * w.open("notes.txt", "w")
10
- * w.writeLine("hello")
11
- * w.writeLine("world")
12
- * w.close()
13
+ * match w.open("notes.txt", "w") {
14
+ * case .ok(ok) -> {
15
+ * w.writeLine("hello")
16
+ * w.writeLine("world")
17
+ * w.close()
18
+ * }
19
+ * case .error(e) -> Console.write("could not open")
20
+ * }
13
21
  *
14
22
  * var File r = File()
15
- * r.open("notes.txt", "r")
16
- * const string first = r.readLine() // "hello"
17
- * const string rest = r.readAll() // read the remaining bytes
23
+ * match r.open("notes.txt", "r") {
24
+ * case .ok(ok) -> {
25
+ * const string first = r.readLine() // Result<string, FileError>
26
+ * }
27
+ * case .error(e) -> Console.write("could not open")
28
+ * }
18
29
  *
19
30
  * Static one-shot helpers do the open/read/close in one call:
20
31
  *
@@ -26,48 +37,132 @@
26
37
  pub struct File {
27
38
  var uint64 handle = 0
28
39
  pub var eof = false
40
+ // errno of the last operation; 0 = success. Raw #arch bodies set it,
41
+ // the Result-returning wrappers map it to FileError.
42
+ var int error = 0
43
+
44
+ // Maps an errno to its FileError case.
45
+ func error_of = (int err, out FileError) {
46
+ switch {
47
+ case err == 2 {
48
+ return FileError.not_found
49
+ }
50
+ case err == 9 {
51
+ return FileError.not_open
52
+ }
53
+ case err == 13 {
54
+ return FileError.access_denied
55
+ }
56
+ case err == 17 {
57
+ return FileError.already_exists
58
+ }
59
+ else {
60
+ return FileError.other
61
+ }
62
+ }
63
+ }
29
64
 
30
65
  // Open the file at `path` with a C mode string ("r", "w", "a", …).
31
- pub func open = (ref self, string path, string mode) {
66
+ // Stores the handle and records errno (0 on success) in `error`.
67
+ func raw_open = (ref self, string path, string mode) {
68
+ ```
69
+ #arch: c
70
+ #scope: file
71
+ #include <errno.h>
72
+ ```
32
73
  ```
33
74
  #arch: c
34
75
  self->handle = (unsigned long long)fopen(path, mode);
76
+ if (self->handle) {
77
+ self->error = 0;
78
+ } else {
79
+ self->error = errno;
80
+ }
35
81
  ```
36
82
  ```
37
83
  #arch: aarch64
84
+ // Fat-string ABI: path = (x1, x2), mode = (x3, x4).
38
85
  mov x0, x1
39
- mov x1, x2
86
+ mov x1, x3
40
87
  bl _fopen
41
88
  str x0, [x19, #8]
89
+ cbnz x0, .Lfile_open_ok
90
+ bl ___error
91
+ ldr w0, [x0]
92
+ str x0, [x19, #24]
93
+ b .Lfile_open_end
94
+ .Lfile_open_ok:
95
+ str xzr, [x19, #24]
96
+ .Lfile_open_end:
42
97
  ```
43
98
  }
44
99
 
45
- // Close the handle. Also performed automatically by #destroy; calling
46
- // close explicitly releases the resource early.
47
- pub func close = (ref self) {
100
+ // Open the file at `path` with a C mode string ("r", "w", "a", …).
101
+ // `.ok(true)` on success; `.error(FileError)` otherwise.
102
+ pub func open = (ref self, string path, string mode, out Result<bool, FileError>) {
103
+ self.raw_open(path, mode)
104
+ if self.error == 0 {
105
+ return Result.ok(true)
106
+ }
107
+ return Result.error(File.error_of(self.error))
108
+ }
109
+
110
+ // Close the handle, recording errno in `error` (0 on success). Also
111
+ // performed automatically by #destroy; calling close explicitly releases
112
+ // the resource early.
113
+ func raw_close = (ref self) {
48
114
  ```
49
115
  #arch: c
50
116
  if (self->handle) {
51
- fclose((FILE*)(unsigned long long)self->handle);
52
- self->handle = 0;
117
+ if (fclose((FILE*)(unsigned long long)self->handle)) {
118
+ self->error = errno;
119
+ } else {
120
+ self->handle = 0;
121
+ self->error = 0;
122
+ }
123
+ } else {
124
+ self->error = 0;
53
125
  }
54
126
  ```
55
127
  ```
56
128
  #arch: aarch64
57
129
  ldr x0, [x19, #8]
58
- cbz x0, .Lfile_close_end
130
+ cbz x0, .Lfile_close_clean
59
131
  bl _fclose
60
- .Lfile_close_end:
132
+ cbnz w0, .Lfile_close_err
61
133
  str xzr, [x19, #8]
134
+ str xzr, [x19, #24]
135
+ b .Lfile_close_end
136
+ .Lfile_close_err:
137
+ bl ___error
138
+ ldr w0, [x0]
139
+ str x0, [x19, #24]
140
+ b .Lfile_close_end
141
+ .Lfile_close_clean:
142
+ str xzr, [x19, #24]
143
+ .Lfile_close_end:
62
144
  ```
63
145
  }
64
146
 
147
+ // Close the handle. Also performed automatically by #destroy; calling
148
+ // close explicitly releases the resource early. Closing an unopened (or
149
+ // already closed) File is `.ok(true)`.
150
+ pub func close = (ref self, out Result<bool, FileError>) {
151
+ self.raw_close()
152
+ if self.error == 0 {
153
+ return Result.ok(true)
154
+ }
155
+ return Result.error(File.error_of(self.error))
156
+ }
157
+
65
158
  // Read every byte from the current position to EOF and return it. Sets
66
- // `eof`.
67
- pub func readAll = (ref self, out string) {
159
+ // `eof` and `error`. Declared `move out string`: the raw body mallocs a
160
+ // fresh buffer and hands ownership to the caller.
161
+ func raw_read_all = (ref self, move out string) {
68
162
  ```
69
163
  #arch: c
70
164
  FILE *f = (FILE*)(unsigned long long)self->handle;
165
+ if (!f) { self->error = 9; return (nomen_string){ NULL, 0 }; }
71
166
  fseek(f, 0, SEEK_END);
72
167
  long size = ftell(f);
73
168
  fseek(f, 0, SEEK_SET);
@@ -75,10 +170,20 @@ pub struct File {
75
170
  fread(buf, 1, size, f);
76
171
  buf[size] = 0;
77
172
  self->eof = 1;
78
- return buf;
173
+ self->error = 0;
174
+ // size bytes were read — the count is exact, no strlen.
175
+ return (nomen_string){ buf, size };
79
176
  ```
80
177
  ```
81
178
  #arch: aarch64
179
+ ldr x0, [x19, #8]
180
+ cbnz x0, .Lfile_read_all_go
181
+ mov x0, #9
182
+ str x0, [x19, #24]
183
+ mov x0, #0
184
+ mov x1, #0
185
+ b .return_File_raw_read_all
186
+ .Lfile_read_all_go:
82
187
  sub sp, sp, #16
83
188
  ldr x0, [x19, #8]
84
189
  mov x1, #0
@@ -105,34 +210,70 @@ pub struct File {
105
210
  strb wzr, [x0, x1]
106
211
  mov w1, #1
107
212
  strb w1, [x19, #16]
213
+ str xzr, [x19, #24]
108
214
  ldr x0, [sp, #8]
215
+ ldr x1, [sp, #0] // fat len half = size read
109
216
  add sp, sp, #16
110
- b .return_File_readAll
217
+ b .return_File_raw_read_all
111
218
  ```
112
219
  return ""
113
220
  }
114
221
 
222
+ // Read every byte from the current position to EOF. `.ok(text)` with the
223
+ // remaining bytes (and `eof` set), or `.error(FileError)` — including
224
+ // `not_open` when the handle was never opened.
225
+ pub func readAll = (ref self, out Result<string, FileError>) {
226
+ const string body = self.raw_read_all()
227
+ if self.error == 0 {
228
+ return Result.ok(body)
229
+ }
230
+ return Result.error(File.error_of(self.error))
231
+ }
232
+
115
233
  // Write a string at the current position.
116
- pub func writeAll = (self, string data) {
234
+ func raw_write_all = (ref self, string data) {
117
235
  ```
118
236
  #arch: c
119
237
  FILE *f = (FILE*)(unsigned long long)self->handle;
238
+ if (!f) { self->error = 9; return; }
120
239
  fputs(data, f);
240
+ self->error = 0;
121
241
  ```
122
242
  ```
123
243
  #arch: aarch64
244
+ // Fat-string ABI: data = (x1, x2).
245
+ ldr x3, [x19, #8]
246
+ cbnz x3, .Lfile_write_all_go
247
+ mov x0, #9
248
+ str x0, [x19, #24]
249
+ b .return_File_raw_write_all
250
+ .Lfile_write_all_go:
124
251
  mov x0, x1
125
- ldr x1, [x19, #8]
252
+ mov x1, x3
126
253
  bl _fputs
254
+ str xzr, [x19, #24]
127
255
  ```
128
256
  }
129
257
 
258
+ // Write a string at the current position. `.ok(true)`, or
259
+ // `.error(FileError)` — including `not_open` when the handle was never
260
+ // opened.
261
+ pub func writeAll = (ref self, string data, out Result<bool, FileError>) {
262
+ self.raw_write_all(data)
263
+ if self.error == 0 {
264
+ return Result.ok(true)
265
+ }
266
+ return Result.error(File.error_of(self.error))
267
+ }
268
+
130
269
  // Read up to the next '\n' (the newline is not included). Sets `eof`
131
- // when the end of the file is reached.
132
- pub func readLine = (ref self, out string) {
270
+ // when the end of the file is reached and `error` on failure. The raw
271
+ // body mallocs a fresh buffer — `move out string` transfers it.
272
+ func raw_read_line = (ref self, move out string) {
133
273
  ```
134
274
  #arch: c
135
275
  FILE *f = (FILE*)(unsigned long long)self->handle;
276
+ if (!f) { self->error = 9; return (nomen_string){ NULL, 0 }; }
136
277
  int cap = 16;
137
278
  int len = 0;
138
279
  char *buf = (char*)malloc(cap);
@@ -147,10 +288,19 @@ pub struct File {
147
288
  }
148
289
  buf[len] = 0;
149
290
  if (c == EOF) self->eof = 1;
150
- return buf;
291
+ self->error = 0;
292
+ return (nomen_string){ buf, len };
151
293
  ```
152
294
  ```
153
295
  #arch: aarch64
296
+ ldr x0, [x19, #8]
297
+ cbnz x0, .Lfile_read_line_go
298
+ mov x0, #9
299
+ str x0, [x19, #24]
300
+ mov x0, #0
301
+ mov x1, #0
302
+ b .return_File_raw_read_line
303
+ .Lfile_read_line_go:
154
304
  sub sp, sp, #32
155
305
  mov x0, #16
156
306
  bl _malloc
@@ -161,24 +311,24 @@ pub struct File {
161
311
  ldr x0, [x19, #8]
162
312
  bl _fgetc
163
313
  str w0, [sp, #24]
164
- .Lfile_readline_loop:
314
+ .Lfile_read_line_loop:
165
315
  ldr w0, [sp, #24]
166
316
  cmp w0, #0
167
- blt .Lfile_readline_done
317
+ blt .Lfile_read_line_done
168
318
  cmp w0, #10
169
- beq .Lfile_readline_done
319
+ beq .Lfile_read_line_done
170
320
  ldr x0, [sp, #16]
171
321
  add x0, x0, #1
172
322
  ldr x1, [sp, #8]
173
323
  cmp x0, x1
174
- ble .Lfile_readline_store
324
+ ble .Lfile_read_line_store
175
325
  ldr x1, [sp, #8]
176
326
  lsl x1, x1, #1
177
327
  str x1, [sp, #8]
178
328
  ldr x0, [sp, #0]
179
329
  bl _realloc
180
330
  str x0, [sp, #0]
181
- .Lfile_readline_store:
331
+ .Lfile_read_line_store:
182
332
  ldr x0, [sp, #0]
183
333
  ldr x1, [sp, #16]
184
334
  ldr w2, [sp, #24]
@@ -189,57 +339,99 @@ pub struct File {
189
339
  ldr x0, [x19, #8]
190
340
  bl _fgetc
191
341
  str w0, [sp, #24]
192
- b .Lfile_readline_loop
193
- .Lfile_readline_done:
342
+ b .Lfile_read_line_loop
343
+ .Lfile_read_line_done:
194
344
  ldr x0, [sp, #0]
195
345
  ldr x1, [sp, #16]
196
346
  strb wzr, [x0, x1]
197
347
  ldr w0, [sp, #24]
198
348
  cmp w0, #0
199
- bge .Lfile_readline_ret
349
+ bge .Lfile_read_line_ret
200
350
  mov w1, #1
201
351
  strb w1, [x19, #16]
202
- .Lfile_readline_ret:
352
+ .Lfile_read_line_ret:
353
+ str xzr, [x19, #24]
203
354
  ldr x0, [sp, #0]
355
+ ldr x1, [sp, #16] // fat len half
204
356
  add sp, sp, #32
205
- b .return_File_readLine
357
+ b .return_File_raw_read_line
206
358
  ```
207
359
  return ""
208
360
  }
209
361
 
362
+ // Read up to the next '\n' (the newline is not included). `.ok(text)`
363
+ // and `eof` set at end of file, or `.error(FileError)`.
364
+ pub func readLine = (ref self, out Result<string, FileError>) {
365
+ const string line = self.raw_read_line()
366
+ if self.error == 0 {
367
+ return Result.ok(line)
368
+ }
369
+ return Result.error(File.error_of(self.error))
370
+ }
371
+
210
372
  // Write a string followed by '\n'.
211
- pub func writeLine = (self, string data) {
373
+ func raw_write_line = (ref self, string data) {
212
374
  ```
213
375
  #arch: c
214
376
  FILE *f = (FILE*)(unsigned long long)self->handle;
377
+ if (!f) { self->error = 9; return; }
215
378
  fputs(data, f);
216
379
  fputc('\n', f);
380
+ self->error = 0;
217
381
  ```
218
382
  ```
219
383
  #arch: aarch64
384
+ // Fat-string ABI: data = (x1, x2).
385
+ ldr x3, [x19, #8]
386
+ cbnz x3, .Lfile_write_line_go
387
+ mov x0, #9
388
+ str x0, [x19, #24]
389
+ b .return_File_raw_write_line
390
+ .Lfile_write_line_go:
220
391
  mov x0, x1
221
- ldr x1, [x19, #8]
392
+ mov x1, x3
222
393
  bl _fputs
223
394
  mov w0, #10
224
395
  ldr x1, [x19, #8]
225
396
  bl _fputc
397
+ str xzr, [x19, #24]
226
398
  ```
227
399
  }
228
400
 
401
+ // Write a string followed by '\n'. `.ok(true)`, or `.error(FileError)`.
402
+ pub func writeLine = (ref self, string data, out Result<bool, FileError>) {
403
+ self.raw_write_line(data)
404
+ if self.error == 0 {
405
+ return Result.ok(true)
406
+ }
407
+ return Result.error(File.error_of(self.error))
408
+ }
409
+
229
410
  // Read up to `size` bytes from the current position. Sets `eof` if fewer
230
- // than `size` bytes remain.
231
- pub func readChunk = (ref self, int size, out string) {
411
+ // than `size` bytes remain and `error` on failure. The raw body mallocs a
412
+ // fresh buffer — `move out string` transfers it.
413
+ func raw_read_chunk = (ref self, int size, move out string) {
232
414
  ```
233
415
  #arch: c
234
416
  FILE *f = (FILE*)(unsigned long long)self->handle;
417
+ if (!f) { self->error = 9; return (nomen_string){ NULL, 0 }; }
235
418
  char *buf = (char*)malloc(size + 1);
236
419
  long n = fread(buf, 1, size, f);
237
420
  buf[n] = 0;
238
421
  if (n < size) self->eof = 1;
239
- return buf;
422
+ self->error = 0;
423
+ return (nomen_string){ buf, n };
240
424
  ```
241
425
  ```
242
426
  #arch: aarch64
427
+ ldr x0, [x19, #8]
428
+ cbnz x0, .Lfile_read_chunk_go
429
+ mov x0, #9
430
+ str x0, [x19, #24]
431
+ mov x0, #0
432
+ mov x1, #0
433
+ b .return_File_raw_read_chunk
434
+ .Lfile_read_chunk_go:
243
435
  sub sp, sp, #16
244
436
  str x1, [sp, #0]
245
437
  ldr x0, [sp, #0]
@@ -255,34 +447,68 @@ pub struct File {
255
447
  strb wzr, [x1, x0]
256
448
  ldr x2, [sp, #0]
257
449
  cmp x0, x2
258
- bge .Lfile_readchunk_ret
450
+ bge .Lfile_read_chunk_ret
259
451
  mov w3, #1
260
452
  strb w3, [x19, #16]
261
- .Lfile_readchunk_ret:
453
+ .Lfile_read_chunk_ret:
454
+ str xzr, [x19, #24]
262
455
  ldr x0, [sp, #8]
456
+ ldr x1, [sp, #0] // fat len half = bytes read
263
457
  add sp, sp, #16
264
- b .return_File_readChunk
458
+ b .return_File_raw_read_chunk
265
459
  ```
266
460
  return ""
267
461
  }
268
462
 
463
+ // Read up to `size` bytes from the current position. `.ok(text)`
464
+ // (`eof` set if fewer bytes remained), or `.error(FileError)`.
465
+ pub func readChunk = (ref self, int size, out Result<string, FileError>) {
466
+ const string chunk = self.raw_read_chunk(size)
467
+ if self.error == 0 {
468
+ return Result.ok(chunk)
469
+ }
470
+ return Result.error(File.error_of(self.error))
471
+ }
472
+
269
473
  // Write the first `size` bytes of `data` at the current position.
270
- pub func writeChunk = (self, string data, int size) {
474
+ func raw_write_chunk = (ref self, string data, int size) {
271
475
  ```
272
476
  #arch: c
273
477
  FILE *f = (FILE*)(unsigned long long)self->handle;
478
+ if (!f) { self->error = 9; return; }
274
479
  fwrite(data, 1, size, f);
480
+ self->error = 0;
275
481
  ```
276
482
  ```
277
483
  #arch: aarch64
484
+ // Fat-string ABI: data = (x1, x2), size in x3.
485
+ ldr x4, [x19, #8]
486
+ cbnz x4, .Lfile_write_chunk_go
487
+ mov x0, #9
488
+ str x0, [x19, #24]
489
+ b .return_File_raw_write_chunk
490
+ .Lfile_write_chunk_go:
278
491
  mov x0, x1
492
+ mov x2, x3
279
493
  mov x1, #1
280
- ldr x3, [x19, #8]
494
+ mov x3, x4
281
495
  bl _fwrite
496
+ str xzr, [x19, #24]
282
497
  ```
283
498
  }
284
499
 
285
- // (static) Whether a file exists at `path`.
500
+ // Write the first `size` bytes of `data` at the current position.
501
+ // `.ok(true)`, or `.error(FileError)`.
502
+ pub func writeChunk = (ref self, string data, int size, out Result<bool, FileError>) {
503
+ self.raw_write_chunk(data, size)
504
+ if self.error == 0 {
505
+ return Result.ok(true)
506
+ }
507
+ return Result.error(File.error_of(self.error))
508
+ }
509
+
510
+ // (static) Whether a file exists at `path`. A missing file is `false`,
511
+ // not an error.
286
512
  pub func exists = (string path, out bool) {
287
513
  ```
288
514
  #arch: c
@@ -302,116 +528,88 @@ pub struct File {
302
528
  return false
303
529
  }
304
530
 
305
- // (static) Remove the file at `path`.
306
- pub func delete = (string path) {
531
+ // (static) Remove the file at `path`; returns errno (0 on success).
532
+ func raw_delete = (string path, out int) {
307
533
  ```
308
534
  #arch: c
309
- remove(path);
535
+ if (remove(path)) return errno;
536
+ return 0;
310
537
  ```
311
538
  ```
312
539
  #arch: aarch64
313
540
  bl _remove
314
- ```
541
+ cbz w0, .Lfile_delete_ok
542
+ bl ___error
543
+ ldr w0, [x0]
544
+ b .return_File_raw_delete
545
+ .Lfile_delete_ok:
546
+ mov x0, #0
547
+ b .return_File_raw_delete
548
+ ```
549
+ return 0
550
+ }
551
+
552
+ // (static) Remove the file at `path`. `.ok(true)`, or
553
+ // `.error(FileError)`.
554
+ pub func delete = (string path, out Result<bool, FileError>) {
555
+ const int err = File.raw_delete(path)
556
+ if err == 0 {
557
+ return Result.ok(true)
558
+ }
559
+ return Result.error(File.error_of(err))
315
560
  }
316
561
 
317
562
  // (static) Read the entire contents of `path` in one open/read/close.
318
- // Returns an empty string if the file cannot be opened.
319
- pub func read_all = (string path, out string) {
320
- ```
321
- #arch: c
322
- FILE *f = fopen(path, "r");
323
- if (!f) return strdup("");
324
- fseek(f, 0, SEEK_END);
325
- long size = ftell(f);
326
- fseek(f, 0, SEEK_SET);
327
- char *buf = (char*)malloc(size + 1);
328
- fread(buf, 1, size, f);
329
- buf[size] = 0;
330
- fclose(f);
331
- return buf;
332
- ```
333
- ```
334
- #arch: aarch64
335
- sub sp, sp, #48
336
- str x0, [sp, #0]
337
- adr x1, .Lfile_read_all_mode
338
- bl _fopen
339
- str x0, [sp, #8]
340
- cbz x0, .Lfile_read_all_empty
341
- ldr x0, [sp, #8]
342
- mov x1, #0
343
- mov x2, #2
344
- bl _fseek
345
- ldr x0, [sp, #8]
346
- bl _ftell
347
- str x0, [sp, #16]
348
- ldr x0, [sp, #8]
349
- mov x1, #0
350
- mov x2, #0
351
- bl _fseek
352
- ldr x0, [sp, #16]
353
- add x0, x0, #1
354
- bl _malloc
355
- str x0, [sp, #24]
356
- ldr x0, [sp, #24]
357
- mov x1, #1
358
- ldr x2, [sp, #16]
359
- ldr x3, [sp, #8]
360
- bl _fread
361
- ldr x0, [sp, #24]
362
- ldr x1, [sp, #16]
363
- strb wzr, [x0, x1]
364
- ldr x0, [sp, #8]
365
- bl _fclose
366
- ldr x0, [sp, #24]
367
- add sp, sp, #48
368
- b .return_File_read_all
369
- .Lfile_read_all_empty:
370
- adr x0, .Lfile_read_all_empty_str
371
- bl _strdup
372
- add sp, sp, #48
373
- b .return_File_read_all
374
- .Lfile_read_all_mode: .asciz "r"
375
- .Lfile_read_all_empty_str: .asciz ""
376
- .p2align 2
377
- ```
378
- return ""
563
+ pub func read_all = (string path, out Result<string, FileError>) {
564
+ var Result<string, FileError> result = .error(FileError.other)
565
+ var File f = File()
566
+ match f.open(path, "r") {
567
+ case .ok(did) {
568
+ match f.readAll() {
569
+ case .ok(text) {
570
+ result = .ok(text)
571
+ }
572
+ case .error(e2) {
573
+ result = .error(e2)
574
+ }
575
+ }
576
+ }
577
+ case .error(e) {
578
+ result = .error(e)
579
+ }
580
+ }
581
+ return result
379
582
  }
380
583
 
381
584
  // (static) Write `data` to `path` (replacing any existing contents) in
382
- // one open/write/close.
383
- pub func write_all = (string path, string data) {
384
- ```
385
- #arch: c
386
- FILE *f = fopen(path, "w");
387
- if (!f) return;
388
- fputs(data, f);
389
- fclose(f);
390
- ```
391
- ```
392
- #arch: aarch64
393
- sub sp, sp, #32
394
- str x0, [sp, #0]
395
- str x1, [sp, #8]
396
- adr x1, .Lfile_write_all_mode
397
- bl _fopen
398
- str x0, [sp, #16]
399
- cbz x0, .Lfile_write_all_done
400
- ldr x0, [sp, #8]
401
- ldr x1, [sp, #16]
402
- bl _fputs
403
- ldr x0, [sp, #16]
404
- bl _fclose
405
- .Lfile_write_all_done:
406
- add sp, sp, #32
407
- b .return_File_write_all
408
- .Lfile_write_all_mode: .asciz "w"
409
- .p2align 2
410
- ```
585
+ // one open/write/close. A write failure is reported as `.error`, not
586
+ // silently swallowed (the `must_use` Result discard check forces this to
587
+ // be deliberate).
588
+ pub func write_all = (string path, string data, out Result<bool, FileError>) {
589
+ var Result<bool, FileError> result = .error(FileError.other)
590
+ var File f = File()
591
+ match f.open(path, "w") {
592
+ case .ok(did) {
593
+ match f.writeAll(data) {
594
+ case .ok(did2) {
595
+ result = f.close()
596
+ }
597
+ case .error(e2) {
598
+ var _ = f.close()
599
+ result = .error(e2)
600
+ }
601
+ }
602
+ }
603
+ case .error(e) {
604
+ result = .error(e)
605
+ }
606
+ }
607
+ return result
411
608
  }
412
609
 
413
610
  // Closes the handle if still open (no-op after close). Runs automatically
414
- // at scope exit, so an unclosed `File` never leaks.
611
+ // at scope exit, so an unclosed `File` never leaks. Best-effort: failures
612
+ // are not reported (use `close` to observe them).
415
613
  func #destroy = () {
416
614
  ```
417
615
  #arch: c