@shd101wyy/yo 0.0.29 → 0.0.31

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 (39) hide show
  1. package/README.md +19 -0
  2. package/out/cjs/index.cjs +7558 -7830
  3. package/out/cjs/yo-cli.cjs +7557 -7829
  4. package/out/esm/index.mjs +7457 -7729
  5. package/out/types/src/codegen/async/runtime-core.d.ts +2 -1
  6. package/out/types/src/codegen/async/runtime-io-common.d.ts +3 -1
  7. package/out/types/src/codegen/async/runtime-io-linux.d.ts +1 -0
  8. package/out/types/src/codegen/async/runtime-io-macos.d.ts +1 -0
  9. package/out/types/src/codegen/async/runtime-io-windows.d.ts +1 -0
  10. package/out/types/src/codegen/async/runtime.d.ts +2 -1
  11. package/out/types/src/codegen/async/state-machine.d.ts +18 -2
  12. package/out/types/src/codegen/functions/context.d.ts +5 -0
  13. package/out/types/src/codegen/parallelism/runtime.d.ts +2 -1
  14. package/out/types/src/codegen/utils/index.d.ts +2 -0
  15. package/out/types/src/expr.d.ts +1 -0
  16. package/out/types/src/target.d.ts +1 -0
  17. package/out/types/tsconfig.tsbuildinfo +1 -1
  18. package/package.json +1 -1
  19. package/std/cli/arg_parser.yo +365 -0
  20. package/std/collections/array_list.yo +108 -0
  21. package/std/collections/hash_map.yo +1 -1
  22. package/std/collections/hash_set.yo +7 -7
  23. package/std/collections/linked_list.yo +1 -1
  24. package/std/encoding/base64.yo +73 -0
  25. package/std/fs/file.yo +113 -6
  26. package/std/fs/types.yo +21 -0
  27. package/std/glob/glob.yo +206 -0
  28. package/std/http/http.yo +196 -0
  29. package/std/io/reader.yo +17 -0
  30. package/std/io/writer.yo +19 -0
  31. package/std/net/tcp.yo +1 -1
  32. package/std/prelude.yo +69 -0
  33. package/std/string/string.yo +398 -4
  34. package/std/sync/cond.yo +19 -19
  35. package/std/sync/mutex.yo +16 -16
  36. package/std/sys/bufio/buf_reader.yo +2 -2
  37. package/std/sys/future.yo +3 -3
  38. package/std/toml/toml.yo +179 -0
  39. package/std/testing/assert.yo +0 -173
package/std/fs/file.yo CHANGED
@@ -34,7 +34,9 @@ open import "../fmt";
34
34
  { Error, AnyError, Exception } :: import "../error";
35
35
  { Metadata } :: import "./metadata";
36
36
  _metadata_mod :: import "./metadata";
37
- { OpenMode, FilePermission, _open_mode_to_flags, _open_mode_needs_perm } :: import "./types";
37
+ { Statx } :: import "../sys/statx";
38
+ IO_path :: import "../sys/path";
39
+ { OpenMode, FilePermission, SeekFrom, _open_mode_to_flags, _open_mode_needs_perm, _seek_from_to_whence } :: import "./types";
38
40
  IO_file :: import "../sys/file";
39
41
  IO_seek :: import "../sys/seek";
40
42
  {
@@ -193,9 +195,8 @@ impl(File,
193
195
  }),
194
196
 
195
197
  // Seek to a position. Returns the new absolute position.
196
- // whence: SEEK_SET(0), SEEK_CUR(1), SEEK_END(2)
197
- seek : (fn(self: Self, offset: i64, whence: i32, using(exn : Exception)) -> i64)({
198
- result := IO_seek.lseek(self._fd, offset, whence);
198
+ seek : (fn(self: Self, offset: i64, from: SeekFrom, using(exn : Exception)) -> i64)({
199
+ result := IO_seek.lseek(self._fd, offset, _seek_from_to_whence(from));
199
200
  cond(
200
201
  (result < i64(0)) => exn.throw(dyn IOError.from_errno(i32((i64(0) - result)))),
201
202
  true => result
@@ -258,7 +259,7 @@ impl(File, Dispose(
258
259
  })
259
260
  ));
260
261
 
261
- export File, OpenMode, FilePermission;
262
+ export File, OpenMode, FilePermission, SeekFrom;
262
263
 
263
264
  // ============================================================================
264
265
  // Convenience functions
@@ -374,11 +375,117 @@ exists_cstr :: (fn(path: *(u8), using(io : IO)) -> Impl(Future(bool, IO)))(
374
375
  exists(Path.from_cstr(path), using(io))
375
376
  );
376
377
 
378
+ // Check if a path is a regular file.
379
+ // Returns false for any error (including file not found).
380
+ is_file :: (fn(path: Path, using(io : IO)) -> Impl(Future(bool, IO)))(
381
+ io.async((using(io : IO)) => {
382
+ path_str := path.to_string();
383
+ cstr_bytes := path_str.to_cstr();
384
+ cstr := cstr_bytes.ptr().unwrap();
385
+ buf_size := __yo_statx_buf_size();
386
+ buf := *(u8)(malloc(buf_size).unwrap());
387
+ result := io.await(IO_file.statx(AT_FDCWD, cstr, AT_STATX_SYNC_AS_STAT, STATX_BASIC_STATS, buf));
388
+ r := cond(
389
+ (result < i32(0)) => false,
390
+ true => {
391
+ sx := Statx(_buf_ptr: buf, _buf_size: buf_size);
392
+ sx.is_file()
393
+ }
394
+ );
395
+ free(.Some(*(void)(buf)));
396
+ r
397
+ })
398
+ );
399
+
400
+ // Check if a path is a regular file (str version).
401
+ is_file_str :: (fn(path: str, using(io : IO)) -> Impl(Future(bool, IO)))(
402
+ is_file(Path.new(String.from(path)), using(io))
403
+ );
404
+
405
+ // Check if a path is a regular file (C string version).
406
+ is_file_cstr :: (fn(path: *(u8), using(io : IO)) -> Impl(Future(bool, IO)))(
407
+ is_file(Path.from_cstr(path), using(io))
408
+ );
409
+
410
+ // Check if a path is a directory.
411
+ // Returns false for any error (including file not found).
412
+ is_dir :: (fn(path: Path, using(io : IO)) -> Impl(Future(bool, IO)))(
413
+ io.async((using(io : IO)) => {
414
+ path_str := path.to_string();
415
+ cstr_bytes := path_str.to_cstr();
416
+ cstr := cstr_bytes.ptr().unwrap();
417
+ buf_size := __yo_statx_buf_size();
418
+ buf := *(u8)(malloc(buf_size).unwrap());
419
+ result := io.await(IO_file.statx(AT_FDCWD, cstr, AT_STATX_SYNC_AS_STAT, STATX_BASIC_STATS, buf));
420
+ r := cond(
421
+ (result < i32(0)) => false,
422
+ true => {
423
+ sx := Statx(_buf_ptr: buf, _buf_size: buf_size);
424
+ sx.is_directory()
425
+ }
426
+ );
427
+ free(.Some(*(void)(buf)));
428
+ r
429
+ })
430
+ );
431
+
432
+ // Check if a path is a directory (str version).
433
+ is_dir_str :: (fn(path: str, using(io : IO)) -> Impl(Future(bool, IO)))(
434
+ is_dir(Path.new(String.from(path)), using(io))
435
+ );
436
+
437
+ // Check if a path is a directory (C string version).
438
+ is_dir_cstr :: (fn(path: *(u8), using(io : IO)) -> Impl(Future(bool, IO)))(
439
+ is_dir(Path.from_cstr(path), using(io))
440
+ );
441
+
442
+ // Resolve a path to its canonical absolute form (resolves symlinks, . and ..).
443
+ // Throws on error (e.g. path does not exist).
444
+ canonical :: (fn(path: Path, using(io : IO)) -> Impl(Future(Path, IO, Exception)))(
445
+ io.async((using(io, exn)) => {
446
+ path_str := path.to_string();
447
+ cstr_bytes := path_str.to_cstr();
448
+ cstr := cstr_bytes.ptr().unwrap();
449
+ // Async stat to verify path exists (provides await point for proper RC cleanup)
450
+ buf_size := __yo_statx_buf_size();
451
+ stat_buf := *(u8)(malloc(buf_size).unwrap());
452
+ stat_result := io.await(IO_file.statx(AT_FDCWD, cstr, AT_STATX_SYNC_AS_STAT, STATX_BASIC_STATS, stat_buf));
453
+ free(.Some(*(void)(stat_buf)));
454
+ IOError.check(stat_result);
455
+ // Resolve the canonical path
456
+ resolved_buf := *(u8)(malloc(usize(4096)).unwrap());
457
+ rp_result := IO_path.realpath(cstr, resolved_buf);
458
+ cond(
459
+ (rp_result < i32(0)) => {
460
+ free(.Some(*(void)(resolved_buf)));
461
+ IOError.check(rp_result);
462
+ },
463
+ true => ()
464
+ );
465
+ resolved := String.from_cstr(resolved_buf).unwrap();
466
+ free(.Some(*(void)(resolved_buf)));
467
+ Path.new(resolved)
468
+ })
469
+ );
470
+
471
+ // Resolve a path to its canonical absolute form (str version).
472
+ canonical_str :: (fn(path: str, using(io : IO)) -> Impl(Future(Path, IO, Exception)))(
473
+ canonical(Path.new(String.from(path)))
474
+ );
475
+
476
+ // Resolve a path to its canonical absolute form (C string version).
477
+ canonical_cstr :: (fn(path: *(u8), using(io : IO)) -> Impl(Future(Path, IO, Exception)))(
478
+ canonical(Path.from_cstr(path))
479
+ );
480
+
377
481
  export
378
482
  read_string, read_string_str, read_string_cstr,
379
483
  read_file, read_file_str, read_file_cstr,
380
484
  write_file, write_file_str, write_file_cstr,
381
485
  write_bytes,
382
486
  append_file, append_file_str,
383
- exists, exists_str, exists_cstr
487
+ exists, exists_str, exists_cstr,
488
+ is_file, is_file_str, is_file_cstr,
489
+ is_dir, is_dir_str, is_dir_cstr,
490
+ canonical, canonical_str, canonical_cstr
384
491
  ;
package/std/fs/types.yo CHANGED
@@ -76,3 +76,24 @@ impl(FilePermission,
76
76
  );
77
77
 
78
78
  export FilePermission;
79
+
80
+ // ============================================================================
81
+ // SeekFrom — file seek position reference
82
+ // ============================================================================
83
+
84
+ SeekFrom :: enum(
85
+ Start, // Seek from the beginning of the file
86
+ Current, // Seek relative to the current position
87
+ End // Seek relative to the end of the file
88
+ );
89
+
90
+ // Convert SeekFrom to POSIX whence constant (i32).
91
+ _seek_from_to_whence :: (fn(from: SeekFrom) -> i32)(
92
+ match(from,
93
+ .Start => i32(0),
94
+ .Current => i32(1),
95
+ .End => i32(2)
96
+ )
97
+ );
98
+
99
+ export SeekFrom, _seek_from_to_whence;
@@ -0,0 +1,206 @@
1
+ open import "../string";
2
+ { ArrayList } :: import "../collections/array_list";
3
+
4
+ // Recursive byte-level glob pattern matching
5
+ _glob_match_impl :: (fn(pb: ArrayList(u8), pi: usize, tb: ArrayList(u8), ti: usize) -> bool)({
6
+ // Base: both pattern and text consumed
7
+ cond(
8
+ ((pi == pb.len()) && (ti == tb.len())) => {
9
+ return true;
10
+ },
11
+ true => ()
12
+ );
13
+
14
+ // Pattern consumed but text remains
15
+ cond(
16
+ (pi == pb.len()) => {
17
+ return false;
18
+ },
19
+ true => ()
20
+ );
21
+
22
+ p := pb.get(pi).unwrap();
23
+
24
+ // Handle * and **
25
+ cond(
26
+ (p == u8(42)) => {
27
+ is_dbl := cond(
28
+ ((pi + usize(1)) < pb.len()) => (pb.get((pi + usize(1))).unwrap() == u8(42)),
29
+ true => false
30
+ );
31
+ cond(
32
+ is_dbl => {
33
+ // ** matches any characters including /
34
+ npi := (pi + usize(2));
35
+ // Skip optional trailing /
36
+ cond(
37
+ (npi < pb.len()) => {
38
+ cond(
39
+ (pb.get(npi).unwrap() == u8(47)) => {
40
+ npi = (npi + usize(1));
41
+ },
42
+ true => ()
43
+ );
44
+ },
45
+ true => ()
46
+ );
47
+ // Try ** matching nothing
48
+ cond(
49
+ recur(pb, npi, tb, ti) => {
50
+ return true;
51
+ },
52
+ true => ()
53
+ );
54
+ // Try ** consuming one char
55
+ cond(
56
+ (ti < tb.len()) => {
57
+ return recur(pb, pi, tb, (ti + usize(1)));
58
+ },
59
+ true => {
60
+ return false;
61
+ }
62
+ );
63
+ },
64
+ true => {
65
+ // Single * matches any except /
66
+ // Try * matching nothing
67
+ cond(
68
+ recur(pb, (pi + usize(1)), tb, ti) => {
69
+ return true;
70
+ },
71
+ true => ()
72
+ );
73
+ // Try * consuming one non-/ char
74
+ cond(
75
+ (ti < tb.len()) => {
76
+ tc := tb.get(ti).unwrap();
77
+ cond(
78
+ (tc != u8(47)) => {
79
+ return recur(pb, pi, tb, (ti + usize(1)));
80
+ },
81
+ true => {
82
+ return false;
83
+ }
84
+ );
85
+ },
86
+ true => {
87
+ return false;
88
+ }
89
+ );
90
+ }
91
+ );
92
+ },
93
+ true => ()
94
+ );
95
+
96
+ // Text consumed but non-star pattern remains
97
+ cond(
98
+ (ti == tb.len()) => {
99
+ return false;
100
+ },
101
+ true => ()
102
+ );
103
+
104
+ t := tb.get(ti).unwrap();
105
+
106
+ // Handle ?
107
+ cond(
108
+ (p == u8(63)) => {
109
+ cond(
110
+ (t != u8(47)) => {
111
+ return recur(pb, (pi + usize(1)), tb, (ti + usize(1)));
112
+ },
113
+ true => {
114
+ return false;
115
+ }
116
+ );
117
+ },
118
+ true => ()
119
+ );
120
+
121
+ // Handle [...]
122
+ cond(
123
+ (p == u8(91)) => {
124
+ ci := (pi + usize(1));
125
+ neg := false;
126
+ // Check for negation: ! or ^
127
+ cond(
128
+ (ci < pb.len()) => {
129
+ nc := pb.get(ci).unwrap();
130
+ cond(
131
+ ((nc == u8(33)) || (nc == u8(94))) => {
132
+ neg = true;
133
+ ci = (ci + usize(1));
134
+ },
135
+ true => ()
136
+ );
137
+ },
138
+ true => ()
139
+ );
140
+ // Scan characters in class until ]
141
+ found := false;
142
+ cdone := false;
143
+ while (((ci < pb.len()) && (!(cdone)))), (ci = (ci + usize(1))), {
144
+ ch := pb.get(ci).unwrap();
145
+ cond(
146
+ (ch == u8(93)) => {
147
+ cdone = true;
148
+ },
149
+ true => {
150
+ cond(
151
+ (ch == t) => {
152
+ found = true;
153
+ },
154
+ true => ()
155
+ );
156
+ }
157
+ );
158
+ };
159
+ // Malformed pattern (no closing ])
160
+ cond(
161
+ (!(cdone)) => {
162
+ return false;
163
+ },
164
+ true => ()
165
+ );
166
+ matched := cond(
167
+ neg => !(found),
168
+ true => found
169
+ );
170
+ cond(
171
+ matched => {
172
+ return recur(pb, ci, tb, (ti + usize(1)));
173
+ },
174
+ true => {
175
+ return false;
176
+ }
177
+ );
178
+ },
179
+ true => ()
180
+ );
181
+
182
+ // Literal byte match
183
+ cond(
184
+ (p == t) => recur(pb, (pi + usize(1)), tb, (ti + usize(1))),
185
+ true => false
186
+ )
187
+ });
188
+
189
+ glob_match :: (fn(pattern: String, text: String) -> bool)(
190
+ _glob_match_impl(pattern._bytes, usize(0), text._bytes, usize(0))
191
+ );
192
+
193
+ GlobPattern :: object(
194
+ _pattern : String
195
+ );
196
+
197
+ impl(GlobPattern,
198
+ new : (fn(pattern: String) -> Self)(
199
+ Self(_pattern: pattern)
200
+ ),
201
+ matches : (fn(self: Self, text: String) -> bool)(
202
+ _glob_match_impl(self._pattern._bytes, usize(0), text._bytes, usize(0))
203
+ )
204
+ );
205
+
206
+ export glob_match, GlobPattern;
@@ -0,0 +1,196 @@
1
+ // std/http/http.yo - HTTP types and request/response builders
2
+
3
+ open import "../string";
4
+ { ArrayList } :: import "../collections/array_list";
5
+ { ToString } :: import "../fmt";
6
+
7
+ HttpMethod :: enum(GET, POST, PUT, DELETE, HEAD, PATCH);
8
+
9
+ impl(HttpMethod, ToString(
10
+ to_string : (self ->
11
+ match(self,
12
+ .GET => `GET`,
13
+ .POST => `POST`,
14
+ .PUT => `PUT`,
15
+ .DELETE => `DELETE`,
16
+ .HEAD => `HEAD`,
17
+ .PATCH => `PATCH`
18
+ )
19
+ )
20
+ ));
21
+
22
+ HttpHeader :: object(name: String, value: String);
23
+
24
+ impl(HttpHeader,
25
+ new : (fn(name: String, value: String) -> Self)(
26
+ Self(name: name, value: value)
27
+ )
28
+ );
29
+
30
+ HttpRequest :: object(
31
+ method: HttpMethod,
32
+ path: String,
33
+ headers: ArrayList(HttpHeader),
34
+ body: String
35
+ );
36
+
37
+ impl(HttpRequest,
38
+ new : (fn(method: HttpMethod, path: String) -> Self)(
39
+ Self(method: method, path: path, headers: ArrayList(HttpHeader).new(), body: ``)
40
+ ),
41
+
42
+ header : (fn(self: Self, name: String, value: String) -> Self)({
43
+ self.headers.push(HttpHeader.new(name, value));
44
+ self
45
+ }),
46
+
47
+ with_body : (fn(self: Self, body: String) -> Self)({
48
+ self.body = body;
49
+ self
50
+ }),
51
+
52
+ set_host : (fn(self: Self, host: String) -> unit)({
53
+ self.headers.push(HttpHeader.new(`Host`, host));
54
+ }),
55
+
56
+ set_header : (fn(self: Self, name: String, value: String) -> unit)({
57
+ self.headers.push(HttpHeader.new(name, value));
58
+ }),
59
+
60
+ set_body : (fn(self: Self, body: String) -> unit)({
61
+ self.body = body;
62
+ }),
63
+
64
+ to_string : (fn(self: Self) -> String)({
65
+ result := `${self.method.to_string()} ${self.path} HTTP/1.1\r\n`;
66
+ i := usize(0);
67
+ while (i < self.headers.len()), (i = (i + usize(1))), {
68
+ h := self.headers.get(i).unwrap();
69
+ result = `${result}${h.name}: ${h.value}\r\n`;
70
+ };
71
+ result = `${result}\r\n`;
72
+ cond(
73
+ (!(self.body.is_empty())) => { result = `${result}${self.body}`; },
74
+ true => ()
75
+ );
76
+ result
77
+ })
78
+ );
79
+
80
+ HttpResponse :: object(
81
+ status_code: i32,
82
+ status_text: String,
83
+ headers: ArrayList(HttpHeader),
84
+ body: String
85
+ );
86
+
87
+ impl(HttpResponse,
88
+ new : (fn(status_code: i32, status_text: String) -> Self)(
89
+ Self(status_code: status_code, status_text: status_text, headers: ArrayList(HttpHeader).new(), body: ``)
90
+ ),
91
+
92
+ get_header : (fn(self: Self, name: String) -> Option(String))({
93
+ lower_name := name.to_lowercase();
94
+ i := usize(0);
95
+ while (i < self.headers.len()), (i = (i + usize(1))), {
96
+ h := self.headers.get(i).unwrap();
97
+ cond((h.name.to_lowercase() == lower_name) => { return .Some(h.value); }, true => ());
98
+ };
99
+ .None
100
+ }),
101
+
102
+ is_ok : (fn(self: Self) -> bool)(
103
+ ((self.status_code >= i32(200)) && (self.status_code < i32(300)))
104
+ ),
105
+
106
+ is_redirect : (fn(self: Self) -> bool)(
107
+ ((self.status_code >= i32(300)) && (self.status_code < i32(400)))
108
+ ),
109
+
110
+ is_error : (fn(self: Self) -> bool)(
111
+ (self.status_code >= i32(400))
112
+ )
113
+ );
114
+
115
+ parse_response :: (fn(raw: String) -> Result(HttpResponse, String))({
116
+ lines := raw.split(`\r\n`);
117
+ cond((lines.len() == usize(0)) => { return .Err(`Empty response`); }, true => ());
118
+
119
+ status_line := lines.get(usize(0)).unwrap();
120
+ cond((!(status_line.starts_with(`HTTP/`))) => { return .Err(`Invalid status line`); }, true => ());
121
+
122
+ sp1_opt := status_line.index_of(` `);
123
+ cond(sp1_opt.is_none() => { return .Err(`Invalid status line format`); }, true => ());
124
+ sp1 := sp1_opt.unwrap();
125
+ rest := status_line.substring((sp1 + usize(1)), status_line.len());
126
+
127
+ sp2_opt := rest.index_of(` `);
128
+ cond(sp2_opt.is_none() => { return .Err(`Invalid status line format`); }, true => ());
129
+ sp2 := sp2_opt.unwrap();
130
+ code_str := rest.substring(usize(0), sp2);
131
+ status_text := rest.substring((sp2 + usize(1)), rest.len());
132
+
133
+ code_opt := code_str.parse_i32();
134
+ cond(code_opt.is_none() => { return .Err(`Invalid status code`); }, true => ());
135
+ code := code_opt.unwrap();
136
+
137
+ resp := HttpResponse.new(code, status_text);
138
+
139
+ j := usize(1);
140
+ body_start := lines.len();
141
+ while (j < lines.len()), (j = (j + usize(1))), {
142
+ hline := lines.get(j).unwrap();
143
+ cond(
144
+ hline.is_empty() => {
145
+ body_start = (j + usize(1));
146
+ break;
147
+ },
148
+ true => {
149
+ colon_opt := hline.index_of(`:`);
150
+ cond(
151
+ colon_opt.is_some() => {
152
+ colon := colon_opt.unwrap();
153
+ hname := hline.substring(usize(0), colon).trim();
154
+ hval := hline.substring((colon + usize(1)), hline.len()).trim();
155
+ resp.headers.push(HttpHeader.new(hname, hval));
156
+ },
157
+ true => ()
158
+ );
159
+ }
160
+ );
161
+ };
162
+
163
+ cond(
164
+ (body_start < lines.len()) => {
165
+ resp.body = lines.get(body_start).unwrap();
166
+ m := (body_start + usize(1));
167
+ while (m < lines.len()), (m = (m + usize(1))), {
168
+ resp.body = `${resp.body}\r\n${lines.get(m).unwrap()}`;
169
+ };
170
+ },
171
+ true => ()
172
+ );
173
+
174
+ .Ok(resp)
175
+ });
176
+
177
+ http_status_text :: (fn(code: i32) -> String)(
178
+ cond(
179
+ (code == i32(200)) => `OK`,
180
+ (code == i32(201)) => `Created`,
181
+ (code == i32(204)) => `No Content`,
182
+ (code == i32(301)) => `Moved Permanently`,
183
+ (code == i32(302)) => `Found`,
184
+ (code == i32(304)) => `Not Modified`,
185
+ (code == i32(400)) => `Bad Request`,
186
+ (code == i32(404)) => `Not Found`,
187
+ (code == i32(405)) => `Method Not Allowed`,
188
+ (code == i32(500)) => `Internal Server Error`,
189
+ (code == i32(502)) => `Bad Gateway`,
190
+ (code == i32(503)) => `Service Unavailable`,
191
+ true => `Unknown`
192
+ )
193
+ );
194
+
195
+ export HttpMethod, HttpHeader, HttpRequest, HttpResponse;
196
+ export parse_response, http_status_text;
@@ -0,0 +1,17 @@
1
+ // std/io/reader.yo - Reader trait for byte-level input
2
+ //
3
+ // Provides a common interface for reading bytes from various sources
4
+ // (files, network streams, buffers, etc.)
5
+
6
+ { Exception } :: import "../error";
7
+
8
+ // Reader trait — synchronous byte reading interface.
9
+ // Types implementing Reader provide `read` for reading into a buffer.
10
+ Reader :: trait(
11
+ // Read up to `size` bytes into buffer `buf`.
12
+ // Returns the number of bytes actually read (0 at end-of-stream).
13
+ read :
14
+ fn(self: *(Self), buf: *(u8), size: usize, using(exn : Exception)) -> usize
15
+ );
16
+
17
+ export Reader;
@@ -0,0 +1,19 @@
1
+ // std/io/writer.yo - Writer trait for byte-level output
2
+ //
3
+ // Provides a common interface for writing bytes to various destinations
4
+ // (files, network streams, buffers, etc.)
5
+
6
+ { Exception } :: import "../error";
7
+
8
+ // Writer trait — synchronous byte writing interface.
9
+ Writer :: trait(
10
+ // Write bytes from buffer. Returns number of bytes written.
11
+ write :
12
+ fn(self: *(Self), buf: *(u8), size: usize, using(exn : Exception)) -> usize,
13
+
14
+ // Flush any buffered data to the underlying destination.
15
+ flush :
16
+ fn(self: *(Self), using(exn : Exception)) -> unit
17
+ );
18
+
19
+ export Writer;
package/std/net/tcp.yo CHANGED
@@ -268,7 +268,7 @@ impl(TcpStream,
268
268
  }),
269
269
 
270
270
  // Read all available data into a byte list.
271
- read_all : (fn(self: Self, using(io : IO)) -> Impl(Future(ArrayList(u8), IO, Exception)))({
271
+ read_bytes : (fn(self: Self, using(io : IO)) -> Impl(Future(ArrayList(u8), IO, Exception)))({
272
272
  fd := self._fd;
273
273
  io.async((using(io : IO, exn : Exception)) => {
274
274
  buf_size := usize(4096);
package/std/prelude.yo CHANGED
@@ -3410,6 +3410,40 @@ impl(forall(T : Type), Option(T),
3410
3410
  )
3411
3411
  );
3412
3412
 
3413
+ // Option(T) implements Comptime when T implements Comptime
3414
+ impl(forall(T : Type), where(T <: Comptime), Option(T), Comptime());
3415
+
3416
+ // Comptime methods for Option(T)
3417
+ impl(forall(T : Type), where(T <: Comptime), Option(T),
3418
+ comptime_unwrap : (fn(comptime(self) : Self) -> comptime(T))(
3419
+ match(self,
3420
+ .Some(value) => value,
3421
+ .None => panic("Called comptime_unwrap on a None value")
3422
+ )
3423
+ ),
3424
+
3425
+ comptime_unwrap_or : (fn(comptime(self) : Self, comptime(or_value) : T) -> comptime(T))(
3426
+ match(self,
3427
+ .Some(value) => value,
3428
+ .None => or_value
3429
+ )
3430
+ ),
3431
+
3432
+ comptime_is_some : (fn(comptime(self) : Self) -> comptime(bool))(
3433
+ match(self,
3434
+ .Some(_) => true,
3435
+ .None => false
3436
+ )
3437
+ ),
3438
+
3439
+ comptime_is_none : (fn(comptime(self) : Self) -> comptime(bool))(
3440
+ match(self,
3441
+ .None => true,
3442
+ .Some(_) => false
3443
+ )
3444
+ )
3445
+ );
3446
+
3413
3447
  // Alias of the Option type
3414
3448
  ? :: Option;
3415
3449
  // Nullable pointer
@@ -3459,6 +3493,41 @@ impl(forall(OkType : Type, ErrorType : Type), Result(OkType, ErrorType),
3459
3493
  )
3460
3494
  )
3461
3495
  );
3496
+
3497
+ // Result(OkType, ErrorType) implements Comptime when both types implement Comptime
3498
+ impl(forall(OkType : Type, ErrorType : Type), where(OkType <: Comptime, ErrorType <: Comptime), Result(OkType, ErrorType), Comptime());
3499
+
3500
+ // Comptime methods for Result(OkType, ErrorType)
3501
+ impl(forall(OkType : Type, ErrorType : Type), where(OkType <: Comptime, ErrorType <: Comptime), Result(OkType, ErrorType),
3502
+ comptime_unwrap : (fn(comptime(self) : Self) -> comptime(OkType))(
3503
+ match(self,
3504
+ .Ok(value) => value,
3505
+ .Err(_) => panic("Called comptime_unwrap on an Err value")
3506
+ )
3507
+ ),
3508
+
3509
+ comptime_unwrap_err : (fn(comptime(self) : Self) -> comptime(ErrorType))(
3510
+ match(self,
3511
+ .Err(error) => error,
3512
+ .Ok(_) => panic("Called comptime_unwrap_err on an Ok value")
3513
+ )
3514
+ ),
3515
+
3516
+ comptime_is_ok : (fn(comptime(self) : Self) -> comptime(bool))(
3517
+ match(self,
3518
+ .Ok(_) => true,
3519
+ .Err(_) => false
3520
+ )
3521
+ ),
3522
+
3523
+ comptime_is_err : (fn(comptime(self) : Self) -> comptime(bool))(
3524
+ match(self,
3525
+ .Err(_) => true,
3526
+ .Ok(_) => false
3527
+ )
3528
+ )
3529
+ );
3530
+
3462
3531
  export
3463
3532
  Result
3464
3533
  ;