mt-lang 0.2.0 → 0.2.1

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 (189) hide show
  1. checksums.yaml +4 -4
  2. data/docs/index.html +4 -3
  3. data/lib/milk_tea/base.rb +1 -1
  4. data/std/asset_pack.mt +267 -0
  5. data/std/async/libuv_runtime.mt +539 -0
  6. data/std/async/mailbox.mt +191 -0
  7. data/std/async/runtime.mt +87 -0
  8. data/std/async.mt +87 -0
  9. data/std/base64.mt +185 -0
  10. data/std/behavior_tree.mt +396 -0
  11. data/std/binary.mt +314 -0
  12. data/std/binary_heap.mt +157 -0
  13. data/std/bitset.mt +239 -0
  14. data/std/box2d.mt +535 -0
  15. data/std/bytes.mt +46 -0
  16. data/std/c/box2d.mt +1254 -0
  17. data/std/c/cgltf.mt +647 -0
  18. data/std/c/cjson.mt +106 -0
  19. data/std/c/crypto.mt +11 -0
  20. data/std/c/crypto_support.h +51 -0
  21. data/std/c/ctype.mt +19 -0
  22. data/std/c/ctype_bindgen.h +29 -0
  23. data/std/c/curl.mt +1223 -0
  24. data/std/c/enet.mt +473 -0
  25. data/std/c/errno.mt +41 -0
  26. data/std/c/errno_bindgen.h +53 -0
  27. data/std/c/flecs.mt +2945 -0
  28. data/std/c/fs.linux.mt +43 -0
  29. data/std/c/fs.windows.mt +43 -0
  30. data/std/c/fs_support.h +954 -0
  31. data/std/c/gl.mt +2065 -0
  32. data/std/c/gl_registry_helpers.h +10748 -0
  33. data/std/c/glfw.mt +499 -0
  34. data/std/c/libc.mt +167 -0
  35. data/std/c/libuv.mt +1491 -0
  36. data/std/c/math.mt +22 -0
  37. data/std/c/math_bindgen.h +31 -0
  38. data/std/c/miniaudio.mt +3420 -0
  39. data/std/c/pcre2.mt +735 -0
  40. data/std/c/process.mt +58 -0
  41. data/std/c/process_support.h +1147 -0
  42. data/std/c/raygui.mt +1602 -0
  43. data/std/c/raylib.mt +1245 -0
  44. data/std/c/raymath.mt +163 -0
  45. data/std/c/rlgl.mt +434 -0
  46. data/std/c/rpng.mt +60 -0
  47. data/std/c/rres.mt +187 -0
  48. data/std/c/sdl3.mt +4132 -0
  49. data/std/c/sqlite3.mt +951 -0
  50. data/std/c/stb_image.mt +52 -0
  51. data/std/c/stb_image_resize2.mt +128 -0
  52. data/std/c/stb_image_write.mt +19 -0
  53. data/std/c/stb_rect_pack.mt +38 -0
  54. data/std/c/stb_truetype.mt +175 -0
  55. data/std/c/stb_vorbis.mt +31 -0
  56. data/std/c/stdio.mt +90 -0
  57. data/std/c/steamworks.h +5769 -0
  58. data/std/c/steamworks.mt +4771 -0
  59. data/std/c/string.mt +16 -0
  60. data/std/c/string_bindgen.h +36 -0
  61. data/std/c/sync.mt +33 -0
  62. data/std/c/sync_support.h +219 -0
  63. data/std/c/terminal.mt +24 -0
  64. data/std/c/terminal_support.h +408 -0
  65. data/std/c/time.mt +26 -0
  66. data/std/c/tls.mt +24 -0
  67. data/std/c/tls_support.h +626 -0
  68. data/std/c/tracy.mt +53 -0
  69. data/std/c/zlib.mt +20 -0
  70. data/std/c/zlib_support.h +239 -0
  71. data/std/c/zstd.mt +231 -0
  72. data/std/cell.mt +60 -0
  73. data/std/cgltf.mt +115 -0
  74. data/std/cjson.mt +90 -0
  75. data/std/cli.mt +768 -0
  76. data/std/cookie.mt +198 -0
  77. data/std/counter.mt +160 -0
  78. data/std/crypto.mt +74 -0
  79. data/std/cstring.mt +13 -0
  80. data/std/ctype.mt +52 -0
  81. data/std/curl/runtime.mt +261 -0
  82. data/std/curl.mt +38 -0
  83. data/std/deque.mt +348 -0
  84. data/std/encoding.mt +93 -0
  85. data/std/enet.mt +133 -0
  86. data/std/env.mt +41 -0
  87. data/std/errno.mt +47 -0
  88. data/std/flecs.mt +978 -0
  89. data/std/fmt.mt +271 -0
  90. data/std/fs.linux.mt +696 -0
  91. data/std/fs.windows.mt +711 -0
  92. data/std/fsm.mt +267 -0
  93. data/std/gl.mt +2062 -0
  94. data/std/glfw.mt +471 -0
  95. data/std/goap.mt +342 -0
  96. data/std/graph.mt +521 -0
  97. data/std/gzip.mt +77 -0
  98. data/std/hash.mt +429 -0
  99. data/std/http/server.mt +810 -0
  100. data/std/http.mt +1227 -0
  101. data/std/intern.mt +53 -0
  102. data/std/jobs.mt +288 -0
  103. data/std/json.mt +627 -0
  104. data/std/libc.mt +34 -0
  105. data/std/libuv.mt +554 -0
  106. data/std/linear_algebra.mt +243 -0
  107. data/std/linked_map.mt +372 -0
  108. data/std/linked_map_view.mt +49 -0
  109. data/std/linked_set.mt +117 -0
  110. data/std/log.mt +69 -0
  111. data/std/map.mt +418 -0
  112. data/std/math.mt +24 -0
  113. data/std/mem/arena.mt +137 -0
  114. data/std/mem/endian.mt +51 -0
  115. data/std/mem/heap.mt +298 -0
  116. data/std/mem/pool.mt +164 -0
  117. data/std/mem/stack.mt +47 -0
  118. data/std/mem/tracking.mt +119 -0
  119. data/std/miniaudio.mt +1299 -0
  120. data/std/multiset.mt +191 -0
  121. data/std/net/channel.mt +727 -0
  122. data/std/net/clock.mt +237 -0
  123. data/std/net/discovery.mt +277 -0
  124. data/std/net/lobby.mt +813 -0
  125. data/std/net/manager.mt +539 -0
  126. data/std/net/mux.mt +839 -0
  127. data/std/net/nat.mt +122 -0
  128. data/std/net/packet.mt +219 -0
  129. data/std/net/punch.mt +162 -0
  130. data/std/net/rpc.mt +119 -0
  131. data/std/net/session.mt +1157 -0
  132. data/std/net/stun.mt +252 -0
  133. data/std/net/sync.mt +183 -0
  134. data/std/net/turn.mt +459 -0
  135. data/std/net.mt +2944 -0
  136. data/std/oauth2.mt +408 -0
  137. data/std/option.mt +51 -0
  138. data/std/ordered_map.mt +497 -0
  139. data/std/ordered_set.mt +348 -0
  140. data/std/path.mt +368 -0
  141. data/std/pcre2/runtime.mt +35 -0
  142. data/std/pcre2.mt +78 -0
  143. data/std/priority_queue.mt +55 -0
  144. data/std/process.mt +837 -0
  145. data/std/queue.mt +55 -0
  146. data/std/random.mt +146 -0
  147. data/std/raygui.mt +99 -0
  148. data/std/raylib/debug_console.mt +71 -0
  149. data/std/raylib/easing.mt +388 -0
  150. data/std/raylib/packed_assets.mt +255 -0
  151. data/std/raylib/runtime.mt +30 -0
  152. data/std/raylib/tracy_gpu.mt +37 -0
  153. data/std/raylib.mt +1510 -0
  154. data/std/raymath.mt +153 -0
  155. data/std/result.mt +89 -0
  156. data/std/rlgl.mt +268 -0
  157. data/std/rpng.mt +46 -0
  158. data/std/rres.mt +36 -0
  159. data/std/sdl3/runtime.mt +54 -0
  160. data/std/sdl3.mt +1731 -0
  161. data/std/serialize.mt +68 -0
  162. data/std/set.mt +124 -0
  163. data/std/spatial.mt +176 -0
  164. data/std/sqlite3.mt +151 -0
  165. data/std/stack.mt +55 -0
  166. data/std/stb_image.mt +47 -0
  167. data/std/stb_image_resize2.mt +41 -0
  168. data/std/stb_image_write.mt +17 -0
  169. data/std/stb_rect_pack.mt +15 -0
  170. data/std/stb_truetype.mt +77 -0
  171. data/std/stb_vorbis.mt +16 -0
  172. data/std/stdio.mt +88 -0
  173. data/std/steamworks.mt +1542 -0
  174. data/std/str.mt +293 -0
  175. data/std/string.mt +234 -0
  176. data/std/sync.mt +194 -0
  177. data/std/tar.mt +704 -0
  178. data/std/terminal.mt +1002 -0
  179. data/std/testing.mt +266 -0
  180. data/std/thread.mt +120 -0
  181. data/std/time.mt +105 -0
  182. data/std/tls.mt +616 -0
  183. data/std/toml.mt +1310 -0
  184. data/std/tracy.mt +42 -0
  185. data/std/uri.mt +118 -0
  186. data/std/url.mt +372 -0
  187. data/std/vec.mt +433 -0
  188. data/std/zstd.mt +94 -0
  189. metadata +187 -2
data/std/process.mt ADDED
@@ -0,0 +1,837 @@
1
+ import std.c.process as c
2
+ import std.mem.arena as arena
3
+ import std.mem.heap as heap
4
+ import std.str as text
5
+ import std.string as string
6
+ import std.vec as vec
7
+
8
+ public struct EnvironmentEntry:
9
+ name: str
10
+ value: str
11
+
12
+ public struct ExitStatus:
13
+ exit_code: long
14
+ term_signal: int
15
+
16
+ public struct CaptureResult:
17
+ stdout: string.String
18
+ stderr: string.String
19
+ status: ExitStatus
20
+
21
+ public struct ProcessError:
22
+ code: int
23
+ message: string.String
24
+
25
+ public struct ReadResult:
26
+ ready: bool
27
+ closed: bool
28
+ data: string.String
29
+
30
+ public struct ChildProcess:
31
+ pid: int
32
+ stdin_fd: int
33
+ stdout_fd: int
34
+ stderr_fd: int
35
+
36
+ public struct PtyProcess:
37
+ pid: int
38
+ master_fd: int
39
+
40
+ struct PreparedCommand:
41
+ storage: arena.Arena
42
+ file: cstr
43
+ args: ptr[ptr[char]]
44
+ env: ptr[ptr[char]]?
45
+ cwd: cstr?
46
+
47
+
48
+ function take_owned_string(data: ptr[char]?, len: ptr_uint) -> string.String:
49
+ if data == null:
50
+ if len != 0:
51
+ fatal(c"process.take_owned_string missing storage")
52
+
53
+ return string.String.create()
54
+
55
+ return unsafe: string.String(data = ptr[ubyte]<-data, len = len, capacity = len, owns_storage = true)
56
+
57
+
58
+ function safe_text_view(value: string.String) -> Option[str]:
59
+ return text.utf8_byte_span_as_str(unsafe: span[ubyte](data = ptr[ubyte]<-value.data, len = value.len))
60
+
61
+
62
+ function empty_environment() -> span[EnvironmentEntry]:
63
+ return zero[span[EnvironmentEntry]]
64
+
65
+
66
+ ## Returns the parent process's environment entries. Each entry is a
67
+ ## "NAME=VALUE" string parsed from the POSIX `environ` array. Entries
68
+ ## with empty names (broken entries) are silently skipped.
69
+ public function parent_environment() -> vec.Vec[EnvironmentEntry]:
70
+ var result = vec.Vec[EnvironmentEntry].create()
71
+ let count = c.mt_process_environ_length()
72
+ if count == 0:
73
+ return result
74
+ result.reserve(count)
75
+ unsafe:
76
+ let raw_ptr_opt = c.mt_process_environ_accessor()
77
+ if raw_ptr_opt == null:
78
+ return result
79
+ let env_array = read(raw_ptr_opt)
80
+ var i: ptr_uint = 0
81
+ while i < count:
82
+ let entry_cstr = read(env_array + i)
83
+ let len = find_null_byte(entry_cstr, 0)
84
+ if len == 0:
85
+ i += 1
86
+ continue
87
+ var eq_pos: ptr_uint = 0
88
+ while eq_pos < len and read(entry_cstr + eq_pos) != 61:
89
+ eq_pos += 1
90
+ if eq_pos == 0 or eq_pos >= len:
91
+ i += 1
92
+ continue
93
+ var nname = string.String.create()
94
+ nname.reserve(eq_pos)
95
+ var ni: ptr_uint = 0
96
+ while ni < eq_pos:
97
+ nname.push_byte(ubyte<-(unsafe: read(entry_cstr + ni)))
98
+ ni += 1
99
+ var nvalue = string.String.create()
100
+ let vlen = len - eq_pos - 1
101
+ if vlen > 0:
102
+ nvalue.reserve(vlen)
103
+ var vi: ptr_uint = 0
104
+ while vi < vlen:
105
+ nvalue.push_byte(ubyte<-(unsafe: read(entry_cstr + eq_pos + 1 + vi)))
106
+ vi += 1
107
+ result.push(EnvironmentEntry(
108
+ name = nname.as_str(),
109
+ value = nvalue.as_str()
110
+ ))
111
+ i += 1
112
+ return result
113
+
114
+
115
+ ## Scan `data` (pointer to a NUL-terminated buffer) from `start` for the
116
+ ## first 0 byte. Returns the byte offset at which the NUL was found.
117
+ function find_null_byte(data: ptr[char], start: ptr_uint) -> ptr_uint:
118
+ var i = start
119
+ unsafe:
120
+ while read(data + i) != 0:
121
+ i += 1
122
+ return i
123
+
124
+
125
+ ## Like `capture` but inherits the parent environment and merges
126
+ ## `extra_env` entries (extra entries override parent entries with the
127
+ ## same name). When `extra_env` is empty, identical to `capture`.
128
+ public function capture_inheriting_env(
129
+ command: span[str],
130
+ cwd: Option[str],
131
+ extra_env: span[EnvironmentEntry]
132
+ ) -> Result[CaptureResult, ProcessError]:
133
+ if extra_env.len == 0:
134
+ return capture_internal(command, cwd, empty_environment())
135
+ var env_vec = parent_environment()
136
+ merge_environment(ref_of(env_vec), extra_env)
137
+ let env_span = env_vec.as_span()
138
+ let result = capture_internal(command, cwd, env_span)
139
+ env_vec.release()
140
+ return result
141
+
142
+
143
+ ## Like `spawn` but inherits the parent environment and merges
144
+ ## `extra_env` entries.
145
+ public function spawn_inheriting_env(
146
+ command: span[str],
147
+ cwd: Option[str],
148
+ extra_env: span[EnvironmentEntry]
149
+ ) -> Result[ChildProcess, ProcessError]:
150
+ if extra_env.len == 0:
151
+ return spawn_internal(command, cwd, empty_environment())
152
+ var env_vec = parent_environment()
153
+ merge_environment(ref_of(env_vec), extra_env)
154
+ let env_span = env_vec.as_span()
155
+ let result = spawn_internal(command, cwd, env_span)
156
+ env_vec.release()
157
+ return result
158
+
159
+
160
+ ## Merge `extra` entries into `base`, overwriting base entries that share
161
+ ## the same name. New entries (names not in base) are appended.
162
+ function merge_environment(base: ref[vec.Vec[EnvironmentEntry]], extra: span[EnvironmentEntry]) -> void:
163
+ var ei: ptr_uint = 0
164
+ while ei < extra.len:
165
+ let entry = unsafe: read(extra.data + ei)
166
+ var found = false
167
+ var bi: ptr_uint = 0
168
+ while bi < base.len():
169
+ let bp = base.get(bi) else:
170
+ break
171
+ if unsafe: read(bp).name.equal(entry.name):
172
+ unsafe: read(bp) = entry
173
+ found = true
174
+ break
175
+ bi += 1
176
+ if not found:
177
+ base.push(entry)
178
+ ei += 1
179
+
180
+
181
+ function pointer_storage_bytes(count: ptr_uint) -> ptr_uint:
182
+ let pointer_size = size_of(ptr[char])
183
+ if heap.mul_overflows(count, pointer_size):
184
+ fatal(c"process pointer storage overflow")
185
+
186
+ return count * pointer_size
187
+
188
+
189
+ function clear_pointer_slot(slot: ptr[ptr[char]]) -> void:
190
+ let slot_bytes = size_of(ptr[char])
191
+ var index: ptr_uint = 0
192
+ unsafe:
193
+ let buffer = ptr[ubyte]<-slot
194
+ while index < slot_bytes:
195
+ read(buffer + index) = 0
196
+ index += 1
197
+
198
+
199
+ function total_storage_bytes(command: span[str], cwd: Option[str], env: span[EnvironmentEntry]) -> ptr_uint:
200
+ var total = pointer_storage_bytes(command.len + 1)
201
+ if env.len != 0:
202
+ total += pointer_storage_bytes(env.len + 1)
203
+
204
+ var index: ptr_uint = 0
205
+ while index < command.len:
206
+ let value = unsafe: read(command.data + index)
207
+ if total > heap.ptr_uint_max - (value.len + 1):
208
+ fatal(c"process command storage overflow")
209
+ total += value.len + 1
210
+ index += 1
211
+
212
+ match cwd:
213
+ Option.some as payload:
214
+ if total > heap.ptr_uint_max - (payload.value.len + 1):
215
+ fatal(c"process cwd storage overflow")
216
+ total += payload.value.len + 1
217
+ Option.none:
218
+ pass
219
+
220
+ index = 0
221
+ while index < env.len:
222
+ let entry = unsafe: read(env.data + index)
223
+ if total > heap.ptr_uint_max - (entry.name.len + 1):
224
+ fatal(c"process env storage overflow")
225
+ total += entry.name.len + 1
226
+ if total > heap.ptr_uint_max - (entry.value.len + 1):
227
+ fatal(c"process env storage overflow")
228
+ total += entry.value.len + 1
229
+ index += 1
230
+
231
+ return total
232
+
233
+
234
+ function write_environment_value(space: ref[arena.Arena], entry: EnvironmentEntry) -> cstr:
235
+ let size_bytes = entry.name.len + entry.value.len + 2
236
+ let memory = space.alloc_bytes(size_bytes) else:
237
+ fatal(c"process env storage exhausted")
238
+
239
+ unsafe:
240
+ let buffer = ptr[char]<-memory
241
+ if entry.name.len != 0:
242
+ heap.copy_bytes(ptr[ubyte]<-buffer, ptr[ubyte]<-entry.name.data, entry.name.len)
243
+ read(ptr[ubyte]<-buffer + entry.name.len) = 61
244
+ if entry.value.len != 0:
245
+ heap.copy_bytes(ptr[ubyte]<-buffer + entry.name.len + 1, ptr[ubyte]<-entry.value.data, entry.value.len)
246
+ read(buffer + entry.name.len + 1 + entry.value.len) = zero[char]
247
+ return cstr<-buffer
248
+
249
+
250
+ function prepare_command(
251
+ command: span[str],
252
+ cwd: Option[str],
253
+ env: span[EnvironmentEntry]
254
+ ) -> Result[PreparedCommand, ProcessError]:
255
+ if command.len == 0:
256
+ return Result[PreparedCommand, ProcessError].failure(error= ProcessError(
257
+ code = -1,
258
+ message = string.String.from_str("process command cannot be empty")
259
+ ))
260
+
261
+ let total_bytes = total_storage_bytes(command, cwd, env)
262
+ var storage = arena.create_aligned(total_bytes, align_of(ptr[char]))
263
+
264
+ let allocated_args = storage.alloc[ptr[char]](command.len + 1) else:
265
+ fatal(c"process args storage exhausted")
266
+
267
+ let args_ptr = unsafe: allocated_args
268
+
269
+ var env_ptr: ptr[ptr[char]]? = null
270
+ var env_storage: ptr[ptr[char]]? = null
271
+ if env.len != 0:
272
+ let allocated = storage.alloc[ptr[char]](env.len + 1) else:
273
+ fatal(c"process env pointer storage exhausted")
274
+
275
+ let allocated_ptr = unsafe: allocated
276
+ env_ptr = allocated_ptr
277
+ env_storage = allocated_ptr
278
+
279
+ var index: ptr_uint = 0
280
+ while index < command.len:
281
+ let value = unsafe: read(command.data + index)
282
+ unsafe: read(args_ptr + index) = ptr[char]<-storage.to_cstr(value)
283
+ index += 1
284
+
285
+ unsafe: clear_pointer_slot(args_ptr + command.len)
286
+
287
+ if env.len != 0:
288
+ let allocated_ptr = env_storage else:
289
+ fatal(c"process env pointer storage missing")
290
+
291
+ let env_storage_ptr = unsafe: allocated_ptr
292
+ index = 0
293
+ while index < env.len:
294
+ let entry = unsafe: read(env.data + index)
295
+ unsafe: read(env_storage_ptr + index) = ptr[char]<-write_environment_value(ref_of(storage), entry)
296
+ index += 1
297
+
298
+ unsafe: clear_pointer_slot(env_storage_ptr + env.len)
299
+
300
+ let file = unsafe: cstr<-read(args_ptr)
301
+ var prepared_cwd: cstr? = null
302
+ match cwd:
303
+ Option.some as payload:
304
+ prepared_cwd = storage.to_cstr(payload.value)
305
+ Option.none:
306
+ pass
307
+
308
+ return Result[PreparedCommand, ProcessError].success(
309
+ value= PreparedCommand(storage = storage, file = file, args = args_ptr, env = env_ptr, cwd = prepared_cwd)
310
+ )
311
+
312
+
313
+ function take_process_error(raw: c.mt_process_error) -> ProcessError:
314
+ if raw.message_data == null and raw.message_len == 0:
315
+ return ProcessError(code = raw.code, message = string.String.from_str("process failed"))
316
+
317
+ return ProcessError(code = raw.code, message = take_owned_string(raw.message_data, raw.message_len))
318
+
319
+
320
+ function simple_process_error(message: str) -> ProcessError:
321
+ return ProcessError(code = -1, message = string.String.from_str(message))
322
+
323
+
324
+ function take_read_result(raw: c.mt_process_read_result) -> ReadResult:
325
+ return ReadResult(ready = raw.ready, closed = raw.closed, data = take_owned_string(raw.data, raw.len))
326
+
327
+
328
+ function take_wait_status(raw: c.mt_process_wait_result) -> ExitStatus:
329
+ return ExitStatus(exit_code = raw.exit_status, term_signal = raw.term_signal)
330
+
331
+
332
+ function close_fd_quiet(fd: int) -> void:
333
+ if fd < 0:
334
+ return
335
+
336
+ var raw_error = zero[c.mt_process_error]
337
+ c.mt_process_close_fd(fd, raw_error)
338
+
339
+
340
+ function close_fd_checked(fd: int) -> Result[bool, ProcessError]:
341
+ if fd < 0:
342
+ return Result[bool, ProcessError].success(value= true)
343
+
344
+ var raw_error = zero[c.mt_process_error]
345
+ let status = c.mt_process_close_fd(fd, raw_error)
346
+ if status != 0:
347
+ return Result[bool, ProcessError].failure(error= take_process_error(raw_error))
348
+
349
+ return Result[bool, ProcessError].success(value= true)
350
+
351
+
352
+ function read_fd_internal(fd: int, timeout_ms: int) -> Result[ReadResult, ProcessError]:
353
+ if fd < 0:
354
+ return Result[ReadResult, ProcessError].failure(error= simple_process_error("process stream is closed"))
355
+
356
+ var raw_result = zero[c.mt_process_read_result]
357
+ var raw_error = zero[c.mt_process_error]
358
+ let status = c.mt_process_read_fd(fd, timeout_ms, raw_result, raw_error)
359
+ if status != 0:
360
+ return Result[ReadResult, ProcessError].failure(error= take_process_error(raw_error))
361
+
362
+ return Result[ReadResult, ProcessError].success(value= take_read_result(raw_result))
363
+
364
+
365
+ function write_fd_internal(fd: int, value: str) -> Result[ptr_uint, ProcessError]:
366
+ if fd < 0:
367
+ return Result[ptr_uint, ProcessError].failure(error= simple_process_error("process stream is closed"))
368
+
369
+ var data: const_ptr[char]? = null
370
+ if value.len != 0:
371
+ data = unsafe: const_ptr[char]<-value.data
372
+
373
+ var written: ptr_uint = 0
374
+ var raw_error = zero[c.mt_process_error]
375
+ let status = c.mt_process_write_fd(fd, data, value.len, written, raw_error)
376
+ if status != 0:
377
+ return Result[ptr_uint, ProcessError].failure(error= take_process_error(raw_error))
378
+
379
+ return Result[ptr_uint, ProcessError].success(value= written)
380
+
381
+
382
+ function wait_internal(pid: int, non_blocking: bool) -> Result[Option[ExitStatus], ProcessError]:
383
+ if pid <= 0:
384
+ return Result[Option[ExitStatus], ProcessError].failure(error= simple_process_error("process pid is invalid"))
385
+
386
+ var raw_result = zero[c.mt_process_wait_result]
387
+ var raw_error = zero[c.mt_process_error]
388
+ var status = 0
389
+ if non_blocking:
390
+ status = c.mt_process_try_wait(pid, raw_result, raw_error)
391
+ else:
392
+ status = c.mt_process_wait(pid, raw_result, raw_error)
393
+
394
+ if status != 0:
395
+ return Result[Option[ExitStatus], ProcessError].failure(error= take_process_error(raw_error))
396
+
397
+ if not raw_result.ready:
398
+ return Result[Option[ExitStatus], ProcessError].success(value= Option[ExitStatus].none)
399
+
400
+ return Result[
401
+ Option[ExitStatus],
402
+ ProcessError
403
+ ].success(value= Option[ExitStatus].some(value= take_wait_status(raw_result)))
404
+
405
+
406
+ function kill_internal(pid: int, signal: int) -> Result[bool, ProcessError]:
407
+ if pid <= 0:
408
+ return Result[bool, ProcessError].failure(error= simple_process_error("process pid is invalid"))
409
+
410
+ var raw_error = zero[c.mt_process_error]
411
+ let status = c.mt_process_kill(pid, signal, raw_error)
412
+ if status != 0:
413
+ return Result[bool, ProcessError].failure(error= take_process_error(raw_error))
414
+
415
+ return Result[bool, ProcessError].success(value= true)
416
+
417
+
418
+ function resize_pty_internal(fd: int, columns: int, rows: int) -> Result[bool, ProcessError]:
419
+ if fd < 0:
420
+ return Result[bool, ProcessError].failure(error= simple_process_error("process stream is closed"))
421
+
422
+ var raw_error = zero[c.mt_process_error]
423
+ let status = c.mt_process_pty_resize(fd, columns, rows, raw_error)
424
+ if status != 0:
425
+ return Result[bool, ProcessError].failure(error= take_process_error(raw_error))
426
+
427
+ return Result[bool, ProcessError].success(value= true)
428
+
429
+
430
+ function spawn_internal(
431
+ command: span[str],
432
+ cwd: Option[str],
433
+ env: span[EnvironmentEntry]
434
+ ) -> Result[ChildProcess, ProcessError]:
435
+ let prepared_result = prepare_command(command, cwd, env)
436
+ match prepared_result:
437
+ Result.failure as payload:
438
+ return Result[ChildProcess, ProcessError].failure(error= payload.error)
439
+ Result.success as payload:
440
+ var prepared = payload.value
441
+ defer prepared.release()
442
+
443
+ var raw_handle = zero[c.mt_process_spawn_handle]
444
+ var raw_error = zero[c.mt_process_error]
445
+ let status = c.mt_process_spawn_interactive(
446
+ prepared.file,
447
+ prepared.args,
448
+ prepared.env,
449
+ prepared.cwd,
450
+ raw_handle,
451
+ raw_error
452
+ )
453
+ if status != 0:
454
+ return Result[ChildProcess, ProcessError].failure(error= take_process_error(raw_error))
455
+
456
+ return Result[ChildProcess, ProcessError].success(
457
+ value= ChildProcess(
458
+ pid = raw_handle.pid,
459
+ stdin_fd = raw_handle.stdin_fd,
460
+ stdout_fd = raw_handle.stdout_fd,
461
+ stderr_fd = raw_handle.stderr_fd
462
+ )
463
+ )
464
+
465
+
466
+ function spawn_pty_internal(
467
+ command: span[str],
468
+ cwd: Option[str],
469
+ env: span[EnvironmentEntry],
470
+ columns: int,
471
+ rows: int
472
+ ) -> Result[PtyProcess, ProcessError]:
473
+ if columns <= 0 or rows <= 0:
474
+ return Result[PtyProcess, ProcessError].failure(error= simple_process_error("pty size must be positive"))
475
+
476
+ let prepared_result = prepare_command(command, cwd, env)
477
+ match prepared_result:
478
+ Result.failure as payload:
479
+ return Result[PtyProcess, ProcessError].failure(error= payload.error)
480
+ Result.success as payload:
481
+ var prepared = payload.value
482
+ defer prepared.release()
483
+
484
+ var raw_handle = zero[c.mt_process_pty_handle]
485
+ var raw_error = zero[c.mt_process_error]
486
+ let status = c.mt_process_spawn_pty(
487
+ prepared.file,
488
+ prepared.args,
489
+ prepared.env,
490
+ prepared.cwd,
491
+ columns,
492
+ rows,
493
+ raw_handle,
494
+ raw_error
495
+ )
496
+ if status != 0:
497
+ return Result[PtyProcess, ProcessError].failure(error= take_process_error(raw_error))
498
+
499
+ return Result[PtyProcess, ProcessError].success(value= PtyProcess(
500
+ pid = raw_handle.pid,
501
+ master_fd = raw_handle.master_fd
502
+ ))
503
+
504
+
505
+ function capture_internal(
506
+ command: span[str],
507
+ cwd: Option[str],
508
+ env: span[EnvironmentEntry]
509
+ ) -> Result[CaptureResult, ProcessError]:
510
+ let prepared_result = prepare_command(command, cwd, env)
511
+ match prepared_result:
512
+ Result.failure as payload:
513
+ return Result[CaptureResult, ProcessError].failure(error= payload.error)
514
+ Result.success as payload:
515
+ var prepared = payload.value
516
+ defer prepared.release()
517
+
518
+ var raw_result = zero[c.mt_process_capture_result]
519
+ var raw_error = zero[c.mt_process_error]
520
+ let status_code = c.mt_process_capture(
521
+ prepared.file,
522
+ prepared.args,
523
+ prepared.env,
524
+ prepared.cwd,
525
+ raw_result,
526
+ raw_error
527
+ )
528
+ if status_code != 0:
529
+ return Result[CaptureResult, ProcessError].failure(error= take_process_error(raw_error))
530
+
531
+ return Result[CaptureResult, ProcessError].success(
532
+ value= CaptureResult(
533
+ stdout = take_owned_string(raw_result.stdout_data, raw_result.stdout_len),
534
+ stderr = take_owned_string(raw_result.stderr_data, raw_result.stderr_len),
535
+ status = ExitStatus(exit_code = raw_result.exit_status, term_signal = raw_result.term_signal)
536
+ )
537
+ )
538
+
539
+
540
+ function spawn_detached_internal(
541
+ command: span[str],
542
+ cwd: Option[str],
543
+ env: span[EnvironmentEntry]
544
+ ) -> Result[int, ProcessError]:
545
+ let prepared_result = prepare_command(command, cwd, env)
546
+ match prepared_result:
547
+ Result.failure as payload:
548
+ return Result[int, ProcessError].failure(error= payload.error)
549
+ Result.success as payload:
550
+ var prepared = payload.value
551
+ defer prepared.release()
552
+
553
+ var pid: int = 0
554
+ var raw_error = zero[c.mt_process_error]
555
+ let status_code = c.mt_process_spawn_detached(
556
+ prepared.file,
557
+ prepared.args,
558
+ prepared.env,
559
+ prepared.cwd,
560
+ pid,
561
+ raw_error
562
+ )
563
+ if status_code != 0:
564
+ return Result[int, ProcessError].failure(error= take_process_error(raw_error))
565
+
566
+ return Result[int, ProcessError].success(value= pid)
567
+
568
+
569
+ extending ExitStatus:
570
+ public function success() -> bool:
571
+ return this.exit_code == 0 and this.term_signal == 0
572
+
573
+
574
+ public function normalized_code() -> int:
575
+ if this.term_signal != 0:
576
+ return 128 + this.term_signal
577
+
578
+ return int<-this.exit_code
579
+
580
+
581
+ extending CaptureResult:
582
+ public function success() -> bool:
583
+ return this.status.success()
584
+
585
+
586
+ public function stdout_text() -> Option[str]:
587
+ return safe_text_view(this.stdout)
588
+
589
+
590
+ public function stderr_text() -> Option[str]:
591
+ return safe_text_view(this.stderr)
592
+
593
+
594
+ public editable function release() -> void:
595
+ this.stdout.release()
596
+ this.stderr.release()
597
+
598
+
599
+ extending ReadResult:
600
+ public function text() -> Option[str]:
601
+ return safe_text_view(this.data)
602
+
603
+
604
+ public function has_data() -> bool:
605
+ return this.data.len != 0
606
+
607
+
608
+ public editable function release() -> void:
609
+ this.data.release()
610
+
611
+
612
+ extending ChildProcess:
613
+ public function read_stdout(timeout_ms: int) -> Result[ReadResult, ProcessError]:
614
+ return read_fd_internal(this.stdout_fd, timeout_ms)
615
+
616
+
617
+ public function read_stderr(timeout_ms: int) -> Result[ReadResult, ProcessError]:
618
+ return read_fd_internal(this.stderr_fd, timeout_ms)
619
+
620
+
621
+ public function write_stdin(value: str) -> Result[ptr_uint, ProcessError]:
622
+ return write_fd_internal(this.stdin_fd, value)
623
+
624
+
625
+ public editable function close_stdin() -> Result[bool, ProcessError]:
626
+ match close_fd_checked(this.stdin_fd):
627
+ Result.failure as payload:
628
+ return Result[bool, ProcessError].failure(error= payload.error)
629
+ Result.success as payload:
630
+ this.stdin_fd = -1
631
+ return Result[bool, ProcessError].success(value= payload.value)
632
+
633
+
634
+ public editable function close_stdout() -> Result[bool, ProcessError]:
635
+ match close_fd_checked(this.stdout_fd):
636
+ Result.failure as payload:
637
+ return Result[bool, ProcessError].failure(error= payload.error)
638
+ Result.success as payload:
639
+ this.stdout_fd = -1
640
+ return Result[bool, ProcessError].success(value= payload.value)
641
+
642
+
643
+ public editable function close_stderr() -> Result[bool, ProcessError]:
644
+ match close_fd_checked(this.stderr_fd):
645
+ Result.failure as payload:
646
+ return Result[bool, ProcessError].failure(error= payload.error)
647
+ Result.success as payload:
648
+ this.stderr_fd = -1
649
+ return Result[bool, ProcessError].success(value= payload.value)
650
+
651
+
652
+ public editable function wait() -> Result[ExitStatus, ProcessError]:
653
+ let wait_result = wait_internal(this.pid, false)
654
+ match wait_result:
655
+ Result.failure as payload:
656
+ return Result[ExitStatus, ProcessError].failure(error= payload.error)
657
+ Result.success as payload:
658
+ match payload.value:
659
+ Option.some as status_payload:
660
+ this.pid = 0
661
+ return Result[ExitStatus, ProcessError].success(value= status_payload.value)
662
+ Option.none:
663
+ return Result[
664
+ ExitStatus,
665
+ ProcessError
666
+ ].failure(error= simple_process_error("process wait returned no status"))
667
+
668
+
669
+ public editable function try_wait() -> Result[Option[ExitStatus], ProcessError]:
670
+ let wait_result = wait_internal(this.pid, true)
671
+ match wait_result:
672
+ Result.failure as payload:
673
+ return Result[Option[ExitStatus], ProcessError].failure(error= payload.error)
674
+ Result.success as payload:
675
+ match payload.value:
676
+ Option.some as status_payload:
677
+ this.pid = 0
678
+ return Result[
679
+ Option[ExitStatus],
680
+ ProcessError
681
+ ].success(value= Option[ExitStatus].some(value= status_payload.value))
682
+ Option.none:
683
+ return Result[Option[ExitStatus], ProcessError].success(value= Option[ExitStatus].none)
684
+
685
+
686
+ public function kill(signal: int) -> Result[bool, ProcessError]:
687
+ return kill_internal(this.pid, signal)
688
+
689
+
690
+ public editable function release() -> void:
691
+ close_fd_quiet(this.stdin_fd)
692
+ close_fd_quiet(this.stdout_fd)
693
+ close_fd_quiet(this.stderr_fd)
694
+ this.stdin_fd = -1
695
+ this.stdout_fd = -1
696
+ this.stderr_fd = -1
697
+
698
+
699
+ extending PtyProcess:
700
+ public function read(timeout_ms: int) -> Result[ReadResult, ProcessError]:
701
+ return read_fd_internal(this.master_fd, timeout_ms)
702
+
703
+
704
+ public function write(value: str) -> Result[ptr_uint, ProcessError]:
705
+ return write_fd_internal(this.master_fd, value)
706
+
707
+
708
+ public function resize(columns: int, rows: int) -> Result[bool, ProcessError]:
709
+ return resize_pty_internal(this.master_fd, columns, rows)
710
+
711
+
712
+ public editable function close() -> Result[bool, ProcessError]:
713
+ match close_fd_checked(this.master_fd):
714
+ Result.failure as payload:
715
+ return Result[bool, ProcessError].failure(error= payload.error)
716
+ Result.success as payload:
717
+ this.master_fd = -1
718
+ return Result[bool, ProcessError].success(value= payload.value)
719
+
720
+
721
+ public editable function wait() -> Result[ExitStatus, ProcessError]:
722
+ let wait_result = wait_internal(this.pid, false)
723
+ match wait_result:
724
+ Result.failure as payload:
725
+ return Result[ExitStatus, ProcessError].failure(error= payload.error)
726
+ Result.success as payload:
727
+ match payload.value:
728
+ Option.some as status_payload:
729
+ this.pid = 0
730
+ return Result[ExitStatus, ProcessError].success(value= status_payload.value)
731
+ Option.none:
732
+ return Result[
733
+ ExitStatus,
734
+ ProcessError
735
+ ].failure(error= simple_process_error("process wait returned no status"))
736
+
737
+
738
+ public editable function try_wait() -> Result[Option[ExitStatus], ProcessError]:
739
+ let wait_result = wait_internal(this.pid, true)
740
+ match wait_result:
741
+ Result.failure as payload:
742
+ return Result[Option[ExitStatus], ProcessError].failure(error= payload.error)
743
+ Result.success as payload:
744
+ match payload.value:
745
+ Option.some as status_payload:
746
+ this.pid = 0
747
+ return Result[
748
+ Option[ExitStatus],
749
+ ProcessError
750
+ ].success(value= Option[ExitStatus].some(value= status_payload.value))
751
+ Option.none:
752
+ return Result[Option[ExitStatus], ProcessError].success(value= Option[ExitStatus].none)
753
+
754
+
755
+ public function kill(signal: int) -> Result[bool, ProcessError]:
756
+ return kill_internal(this.pid, signal)
757
+
758
+
759
+ public editable function release() -> void:
760
+ close_fd_quiet(this.master_fd)
761
+ this.master_fd = -1
762
+
763
+
764
+ extending ProcessError:
765
+ public editable function release() -> void:
766
+ this.message.release()
767
+
768
+
769
+ extending PreparedCommand:
770
+ editable function release() -> void:
771
+ this.storage.release()
772
+
773
+
774
+ public function capture(command: span[str]) -> Result[CaptureResult, ProcessError]:
775
+ return capture_internal(command, Option[str].none, empty_environment())
776
+
777
+
778
+ public function capture_in(command: span[str], cwd: str) -> Result[CaptureResult, ProcessError]:
779
+ return capture_internal(command, Option[str].some(value= cwd), empty_environment())
780
+
781
+
782
+ public function capture_with_env(
783
+ command: span[str],
784
+ cwd: Option[str],
785
+ env: span[EnvironmentEntry]
786
+ ) -> Result[CaptureResult, ProcessError]:
787
+ return capture_internal(command, cwd, env)
788
+
789
+
790
+ public function spawn(command: span[str]) -> Result[ChildProcess, ProcessError]:
791
+ return spawn_internal(command, Option[str].none, empty_environment())
792
+
793
+
794
+ public function spawn_in(command: span[str], cwd: str) -> Result[ChildProcess, ProcessError]:
795
+ return spawn_internal(command, Option[str].some(value= cwd), empty_environment())
796
+
797
+
798
+ public function spawn_with_env(
799
+ command: span[str],
800
+ cwd: Option[str],
801
+ env: span[EnvironmentEntry]
802
+ ) -> Result[ChildProcess, ProcessError]:
803
+ return spawn_internal(command, cwd, env)
804
+
805
+
806
+ public function spawn_pty(command: span[str], columns: int, rows: int) -> Result[PtyProcess, ProcessError]:
807
+ return spawn_pty_internal(command, Option[str].none, empty_environment(), columns, rows)
808
+
809
+
810
+ public function spawn_pty_in(command: span[str], cwd: str, columns: int, rows: int) -> Result[PtyProcess, ProcessError]:
811
+ return spawn_pty_internal(command, Option[str].some(value= cwd), empty_environment(), columns, rows)
812
+
813
+
814
+ public function spawn_pty_with_env(
815
+ command: span[str],
816
+ cwd: Option[str],
817
+ env: span[EnvironmentEntry],
818
+ columns: int,
819
+ rows: int
820
+ ) -> Result[PtyProcess, ProcessError]:
821
+ return spawn_pty_internal(command, cwd, env, columns, rows)
822
+
823
+
824
+ public function spawn_detached(command: span[str]) -> Result[int, ProcessError]:
825
+ return spawn_detached_internal(command, Option[str].none, empty_environment())
826
+
827
+
828
+ public function spawn_detached_in(command: span[str], cwd: str) -> Result[int, ProcessError]:
829
+ return spawn_detached_internal(command, Option[str].some(value= cwd), empty_environment())
830
+
831
+
832
+ public function spawn_detached_with_env(
833
+ command: span[str],
834
+ cwd: Option[str],
835
+ env: span[EnvironmentEntry]
836
+ ) -> Result[int, ProcessError]:
837
+ return spawn_detached_internal(command, cwd, env)