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/cli.mt ADDED
@@ -0,0 +1,768 @@
1
+ import std.fmt as fmt
2
+ import std.str as text
3
+ import std.string as string
4
+ import std.vec as vec
5
+
6
+ const byte_equals: ubyte = 61ub
7
+
8
+ public enum OptionKind: int
9
+ flag = 0
10
+ value = 1
11
+
12
+ public struct OptionSpec:
13
+ long_name: str
14
+ short_name: Option[str]
15
+ kind: OptionKind
16
+ help: str
17
+ value_name: Option[str]
18
+ default_value: Option[str]
19
+ required: bool
20
+
21
+ public struct CommandSpec:
22
+ name: str
23
+ summary: str
24
+ help: str
25
+ options: span[OptionSpec]
26
+ positional_help: Option[str]
27
+
28
+ public struct AppSpec:
29
+ name: str
30
+ summary: str
31
+ options: span[OptionSpec]
32
+ commands: span[CommandSpec]
33
+ positional_help: Option[str]
34
+
35
+ public struct OptionMatch:
36
+ name: string.String
37
+ present: bool
38
+ value: string.String
39
+ has_value: bool
40
+
41
+ public struct Match:
42
+ command_name: string.String
43
+ has_command: bool
44
+ options: vec.Vec[OptionMatch]
45
+ positionals: vec.Vec[string.String]
46
+ show_help: bool
47
+
48
+ public struct Error:
49
+ message: string.String
50
+
51
+
52
+ function create_error(message: str) -> Error:
53
+ return Error(message = string.String.from_str(message))
54
+
55
+
56
+ function create_option_match(spec: OptionSpec) -> OptionMatch:
57
+ var value = string.String.create()
58
+ var has_value = false
59
+ match spec.default_value:
60
+ Option.some as payload:
61
+ value = string.String.from_str(payload.value)
62
+ has_value = true
63
+ Option.none:
64
+ pass
65
+
66
+ return OptionMatch(
67
+ name = string.String.from_str(spec.long_name),
68
+ present = false,
69
+ value = value,
70
+ has_value = has_value
71
+ )
72
+
73
+
74
+ function create_match() -> Match:
75
+ return Match(
76
+ command_name = string.String.create(),
77
+ has_command = false,
78
+ options = vec.Vec[OptionMatch].create(),
79
+ positionals = vec.Vec[string.String].create(),
80
+ show_help = false
81
+ )
82
+
83
+
84
+ function append_line(output: ref[string.String], line: str) -> void:
85
+ output.append_format(f"#{line}\n")
86
+
87
+
88
+ function append_switch_label(output: ref[string.String], spec: OptionSpec) -> void:
89
+ match spec.short_name:
90
+ Option.some as payload:
91
+ output.append_format(f"-#{payload.value}, ")
92
+ Option.none:
93
+ pass
94
+
95
+ output.append_format(f"--#{spec.long_name}")
96
+
97
+ if spec.kind == OptionKind.value:
98
+ match spec.value_name:
99
+ Option.some as payload:
100
+ output.append_format(f" #{payload.value}")
101
+ Option.none:
102
+ output.append(" VALUE")
103
+
104
+
105
+ function append_option_help(output: ref[string.String], spec: OptionSpec) -> void:
106
+ output.append(" ")
107
+ append_switch_label(output, spec)
108
+ output.append_format(f" - #{spec.help}")
109
+ match spec.default_value:
110
+ Option.some as payload:
111
+ output.append_format(f" (default: #{payload.value})")
112
+ Option.none:
113
+ pass
114
+ if spec.required:
115
+ output.append(" (required)")
116
+ output.append("\n")
117
+
118
+
119
+ function append_help_header(output: ref[string.String], title: str, summary: str, usage: str) -> void:
120
+ append_line(output, title)
121
+ append_line(output, usage)
122
+ append_line(output, "")
123
+ append_line(output, summary)
124
+ append_line(output, "")
125
+
126
+
127
+ function append_commands_section(output: ref[string.String], commands: span[CommandSpec]) -> void:
128
+ if commands.len == 0:
129
+ return
130
+
131
+ append_line(output, "Commands:")
132
+ var index: ptr_uint = 0
133
+ while index < commands.len:
134
+ let spec = unsafe: read(commands.data + index)
135
+ output.append_format(f" #{spec.name} - #{spec.summary}\n")
136
+ index += 1
137
+ append_line(output, "")
138
+
139
+
140
+ function append_options_section(
141
+ output: ref[string.String],
142
+ app_options: span[OptionSpec],
143
+ command_options: span[OptionSpec]
144
+ ) -> void:
145
+ append_line(output, "Options:")
146
+ append_line(output, " -h, --help - Show this help")
147
+
148
+ var index: ptr_uint = 0
149
+ while index < app_options.len:
150
+ append_option_help(output, unsafe: read(app_options.data + index))
151
+ index += 1
152
+
153
+ index = 0
154
+ while index < command_options.len:
155
+ append_option_help(output, unsafe: read(command_options.data + index))
156
+ index += 1
157
+
158
+
159
+ function short_name_valid(short_name: str) -> bool:
160
+ return short_name.len == 1
161
+
162
+
163
+ function find_command(commands: span[CommandSpec], name: str) -> ptr[CommandSpec]?:
164
+ var index: ptr_uint = 0
165
+ while index < commands.len:
166
+ let candidate = unsafe: commands.data + index
167
+ if unsafe: read(candidate).name.equal(name):
168
+ return candidate
169
+ index += 1
170
+
171
+ return null
172
+
173
+
174
+ function find_long_option(
175
+ app_options: span[OptionSpec],
176
+ command_options: span[OptionSpec],
177
+ name: str
178
+ ) -> ptr[OptionSpec]?:
179
+ var index: ptr_uint = 0
180
+ while index < command_options.len:
181
+ let candidate = unsafe: command_options.data + index
182
+ if unsafe: read(candidate).long_name.equal(name):
183
+ return candidate
184
+ index += 1
185
+
186
+ index = 0
187
+ while index < app_options.len:
188
+ let candidate = unsafe: app_options.data + index
189
+ if unsafe: read(candidate).long_name.equal(name):
190
+ return candidate
191
+ index += 1
192
+
193
+ return null
194
+
195
+
196
+ function find_short_option(
197
+ app_options: span[OptionSpec],
198
+ command_options: span[OptionSpec],
199
+ name: str
200
+ ) -> ptr[OptionSpec]?:
201
+ var index: ptr_uint = 0
202
+ while index < command_options.len:
203
+ let candidate = unsafe: command_options.data + index
204
+ match unsafe: read(candidate).short_name:
205
+ Option.some as payload:
206
+ if payload.value.equal(name):
207
+ return candidate
208
+ Option.none:
209
+ pass
210
+ index += 1
211
+
212
+ index = 0
213
+ while index < app_options.len:
214
+ let candidate = unsafe: app_options.data + index
215
+ match unsafe: read(candidate).short_name:
216
+ Option.some as payload:
217
+ if payload.value.equal(name):
218
+ return candidate
219
+ Option.none:
220
+ pass
221
+ index += 1
222
+
223
+ return null
224
+
225
+
226
+ function find_option_match(matches: vec.Vec[OptionMatch], name: str) -> ptr[OptionMatch]?:
227
+ var index: ptr_uint = 0
228
+ while true:
229
+ if index >= matches.len():
230
+ break
231
+
232
+ let candidate = matches.get(index) else:
233
+ return null
234
+
235
+ if unsafe: read(candidate).name.as_str().equal(name):
236
+ return candidate
237
+
238
+ index += 1
239
+
240
+ return null
241
+
242
+
243
+ function append_option_specs(matches: ptr[vec.Vec[OptionMatch]], specs: span[OptionSpec]) -> Result[bool, Error]:
244
+ var index: ptr_uint = 0
245
+ while index < specs.len:
246
+ let spec = unsafe: read(specs.data + index)
247
+
248
+ if spec.long_name.len == 0:
249
+ return Result[bool, Error].failure(error= create_error("cli option long name cannot be empty"))
250
+
251
+ match spec.short_name:
252
+ Option.some as payload:
253
+ if not short_name_valid(payload.value):
254
+ return Result[
255
+ bool,
256
+ Error
257
+ ].failure(error= create_error("cli short option names must be exactly one character"))
258
+ Option.none:
259
+ pass
260
+
261
+ let current = unsafe: read(matches)
262
+ if find_option_match(current, spec.long_name) != null:
263
+ return Result[
264
+ bool,
265
+ Error
266
+ ].failure(error= create_error("cli option names must be unique within the active command"))
267
+
268
+ unsafe: read(matches).push(create_option_match(spec))
269
+ index += 1
270
+
271
+ return Result[bool, Error].success(value= true)
272
+
273
+
274
+ function set_option_flag(result: ptr[Match], spec: OptionSpec) -> Result[bool, Error]:
275
+ let current = unsafe: read(result)
276
+ let option_match = find_option_match(current.options, spec.long_name) else:
277
+ return Result[bool, Error].failure(error= create_error("cli parser lost option state"))
278
+
279
+ unsafe:
280
+ read(option_match).present = true
281
+ if spec.kind == OptionKind.flag:
282
+ read(option_match).has_value = false
283
+ read(option_match).value.clear()
284
+
285
+ return Result[bool, Error].success(value= true)
286
+
287
+
288
+ function set_option_value(result: ptr[Match], spec: OptionSpec, value: str) -> Result[bool, Error]:
289
+ let current = unsafe: read(result)
290
+ let option_match = find_option_match(current.options, spec.long_name) else:
291
+ return Result[bool, Error].failure(error= create_error("cli parser lost option state"))
292
+
293
+ unsafe:
294
+ read(option_match).present = true
295
+ read(option_match).has_value = true
296
+ read(option_match).value.assign(value)
297
+
298
+ return Result[bool, Error].success(value= true)
299
+
300
+
301
+ function require_value_arg(option_name: str) -> Error:
302
+ let message = fmt.format(f"missing value for #{option_name}")
303
+ return Error(message = message)
304
+
305
+
306
+ function required_option_error(option_name: str) -> Error:
307
+ let message = fmt.format(f"required option #{option_name} is missing")
308
+ return Error(message = message)
309
+
310
+
311
+ function unknown_option_error(option_name: str) -> Error:
312
+ let message = fmt.format(f"unknown option #{option_name}")
313
+ return Error(message = message)
314
+
315
+
316
+ function unknown_command_error(command_name: str) -> Error:
317
+ let message = fmt.format(f"unknown command #{command_name}")
318
+ return Error(message = message)
319
+
320
+
321
+ function option_takes_value(spec: OptionSpec) -> bool:
322
+ return spec.kind == OptionKind.value
323
+
324
+
325
+ function split_long_option(arg: str) -> Option[ptr_uint]:
326
+ match arg.find_byte(byte_equals):
327
+ Option.some as payload:
328
+ if payload.value > 2:
329
+ return Option[ptr_uint].some(value= payload.value)
330
+ Option.none:
331
+ pass
332
+
333
+ return Option[ptr_uint].none
334
+
335
+
336
+ function usage_for_app(app: AppSpec) -> string.String:
337
+ var usage = string.String.create()
338
+ usage.append_format(f"Usage: #{app.name}")
339
+ if app.options.len != 0:
340
+ usage.append(" [options]")
341
+ if app.commands.len != 0:
342
+ usage.append(" <command>")
343
+ else:
344
+ match app.positional_help:
345
+ Option.some as payload:
346
+ usage.append(" ")
347
+ usage.append(payload.value)
348
+ Option.none:
349
+ pass
350
+ return usage
351
+
352
+
353
+ function usage_for_command(app: AppSpec, command: CommandSpec) -> string.String:
354
+ var usage = string.String.create()
355
+ usage.append_format(f"Usage: #{app.name} #{command.name}")
356
+ if app.options.len != 0 or command.options.len != 0:
357
+ usage.append(" [options]")
358
+ match command.positional_help:
359
+ Option.some as payload:
360
+ usage.append(" ")
361
+ usage.append(payload.value)
362
+ Option.none:
363
+ pass
364
+ return usage
365
+
366
+
367
+ function validate_required_options(
368
+ result: Match,
369
+ app_options: span[OptionSpec],
370
+ command_options: span[OptionSpec]
371
+ ) -> Result[bool, Error]:
372
+ var index: ptr_uint = 0
373
+ while index < app_options.len:
374
+ let spec = unsafe: read(app_options.data + index)
375
+ if spec.required:
376
+ let option_match = find_option_match(result.options, spec.long_name) else:
377
+ return Result[bool, Error].failure(error= create_error("cli parser lost global option state"))
378
+
379
+ let current = unsafe: read(option_match)
380
+ if not current.present and not current.has_value:
381
+ return Result[bool, Error].failure(error= required_option_error(spec.long_name))
382
+ index += 1
383
+
384
+ index = 0
385
+ while index < command_options.len:
386
+ let spec = unsafe: read(command_options.data + index)
387
+ if spec.required:
388
+ let option_match = find_option_match(result.options, spec.long_name) else:
389
+ return Result[bool, Error].failure(error= create_error("cli parser lost command option state"))
390
+
391
+ let current = unsafe: read(option_match)
392
+ if not current.present and not current.has_value:
393
+ return Result[bool, Error].failure(error= required_option_error(spec.long_name))
394
+ index += 1
395
+
396
+ return Result[bool, Error].success(value= true)
397
+
398
+
399
+ public function flag_option(long_name: str, short_name: Option[str], help: str) -> OptionSpec:
400
+ return OptionSpec(
401
+ long_name = long_name,
402
+ short_name = short_name,
403
+ kind = OptionKind.flag,
404
+ help = help,
405
+ value_name = Option[str].none,
406
+ default_value = Option[str].none,
407
+ required = false
408
+ )
409
+
410
+
411
+ public function value_option(
412
+ long_name: str,
413
+ short_name: Option[str],
414
+ value_name: str,
415
+ help: str,
416
+ required: bool,
417
+ default_value: Option[str]
418
+ ) -> OptionSpec:
419
+ return OptionSpec(
420
+ long_name = long_name,
421
+ short_name = short_name,
422
+ kind = OptionKind.value,
423
+ help = help,
424
+ value_name = Option[str].some(value= value_name),
425
+ default_value = default_value,
426
+ required = required
427
+ )
428
+
429
+
430
+ public function command_spec(
431
+ name: str,
432
+ summary: str,
433
+ help: str,
434
+ options: span[OptionSpec],
435
+ positional_help: Option[str]
436
+ ) -> CommandSpec:
437
+ return CommandSpec(
438
+ name = name,
439
+ summary = summary,
440
+ help = help,
441
+ options = options,
442
+ positional_help = positional_help
443
+ )
444
+
445
+
446
+ public function app_spec(
447
+ name: str,
448
+ summary: str,
449
+ options: span[OptionSpec],
450
+ commands: span[CommandSpec],
451
+ positional_help: Option[str]
452
+ ) -> AppSpec:
453
+ return AppSpec(
454
+ name = name,
455
+ summary = summary,
456
+ options = options,
457
+ commands = commands,
458
+ positional_help = positional_help
459
+ )
460
+
461
+
462
+ function parse_long_option_impl(
463
+ result: ptr[Match],
464
+ app_options: span[OptionSpec],
465
+ command_options: span[OptionSpec],
466
+ arg: str,
467
+ next_arg: Option[str]
468
+ ) -> Result[ptr_uint, Error]:
469
+ let split_index = split_long_option(arg)
470
+ var option_name = arg.slice(2, arg.len - 2)
471
+ var inline_value = Option[str].none
472
+ match split_index:
473
+ Option.some as payload:
474
+ option_name = arg.slice(2, payload.value - 2)
475
+ inline_value = Option[str].some(value= arg.slice(payload.value + 1, arg.len - payload.value - 1))
476
+ Option.none:
477
+ pass
478
+
479
+ let spec_ptr = find_long_option(app_options, command_options, option_name) else:
480
+ return Result[ptr_uint, Error].failure(error= unknown_option_error(arg))
481
+
482
+ let spec = unsafe: read(spec_ptr)
483
+ if option_takes_value(spec):
484
+ var consumed = 1z
485
+
486
+ match inline_value:
487
+ Option.some as inline_payload:
488
+ match set_option_value(result, spec, inline_payload.value):
489
+ Result.failure as set_error:
490
+ return Result[ptr_uint, Error].failure(error= set_error.error)
491
+ Result.success:
492
+ return Result[ptr_uint, Error].success(value= consumed)
493
+ Option.none:
494
+ pass
495
+
496
+ let value_arg = next_arg else:
497
+ let missing = fmt.format(f"--#{spec.long_name}")
498
+ return Result[ptr_uint, Error].failure(error= require_value_arg(missing.as_str()))
499
+
500
+ match set_option_value(result, spec, value_arg):
501
+ Result.failure as payload:
502
+ return Result[ptr_uint, Error].failure(error= payload.error)
503
+ Result.success:
504
+ consumed = 2z
505
+ return Result[ptr_uint, Error].success(value= consumed)
506
+
507
+ match inline_value:
508
+ Option.some:
509
+ return Result[ptr_uint, Error].failure(error= create_error("flag options do not accept inline values"))
510
+ Option.none:
511
+ pass
512
+
513
+ match set_option_flag(result, spec):
514
+ Result.failure as payload:
515
+ return Result[ptr_uint, Error].failure(error= payload.error)
516
+ Result.success:
517
+ return Result[ptr_uint, Error].success(value= 1z)
518
+
519
+
520
+ function parse_short_option_impl(
521
+ result: ptr[Match],
522
+ app_options: span[OptionSpec],
523
+ command_options: span[OptionSpec],
524
+ arg: str,
525
+ next_arg: Option[str]
526
+ ) -> Result[ptr_uint, Error]:
527
+ var short_index: ptr_uint = 1
528
+ while short_index < arg.len:
529
+ let short_name = arg.slice(short_index, 1)
530
+
531
+ let spec_ptr = find_short_option(app_options, command_options, short_name) else:
532
+ return Result[ptr_uint, Error].failure(error= unknown_option_error(arg))
533
+
534
+ let spec = unsafe: read(spec_ptr)
535
+ if option_takes_value(spec):
536
+ var consumed = 1z
537
+
538
+ if short_index + 1 < arg.len:
539
+ let value = arg.slice(short_index + 1, arg.len - short_index - 1)
540
+ match set_option_value(result, spec, value):
541
+ Result.failure as payload:
542
+ return Result[ptr_uint, Error].failure(error= payload.error)
543
+ Result.success:
544
+ return Result[ptr_uint, Error].success(value= consumed)
545
+
546
+ let value_arg = next_arg else:
547
+ let missing = fmt.format(f"-#{short_name}")
548
+ return Result[ptr_uint, Error].failure(error= require_value_arg(missing.as_str()))
549
+
550
+ match set_option_value(result, spec, value_arg):
551
+ Result.failure as payload:
552
+ return Result[ptr_uint, Error].failure(error= payload.error)
553
+ Result.success:
554
+ consumed = 2z
555
+ return Result[ptr_uint, Error].success(value= consumed)
556
+
557
+ match set_option_flag(result, spec):
558
+ Result.failure as payload:
559
+ return Result[ptr_uint, Error].failure(error= payload.error)
560
+ Result.success:
561
+ pass
562
+
563
+ short_index += 1
564
+
565
+ return Result[ptr_uint, Error].success(value= 1z)
566
+
567
+
568
+ public function parse(app: AppSpec, args: span[str]) -> Result[Match, Error]:
569
+ var result = create_match()
570
+
571
+ match append_option_specs(ptr_of(result.options), app.options):
572
+ Result.failure as append_error_payload:
573
+ result.release()
574
+ return Result[Match, Error].failure(error= append_error_payload.error)
575
+ Result.success:
576
+ pass
577
+
578
+ var command_options: span[OptionSpec] = zero[span[OptionSpec]]
579
+ var index: ptr_uint = 0
580
+ var end_of_options = false
581
+
582
+ while index < args.len:
583
+ let arg = unsafe: read(args.data + index)
584
+
585
+ if not end_of_options and arg.equal("--"):
586
+ end_of_options = true
587
+ index += 1
588
+ continue
589
+
590
+ if not end_of_options and (arg.equal("--help") or arg.equal("-h")):
591
+ result.show_help = true
592
+ return Result[Match, Error].success(value= result)
593
+
594
+ if not end_of_options and arg.starts_with("--") and arg.len > 2:
595
+ var next_arg = Option[str].none
596
+ if index + 1 < args.len:
597
+ next_arg = Option[str].some(value = unsafe: read(args.data + index + 1))
598
+
599
+ match parse_long_option_impl(ptr_of(result), app.options, command_options, arg, next_arg):
600
+ Result.failure as payload:
601
+ result.release()
602
+ return Result[Match, Error].failure(error= payload.error)
603
+ Result.success as consumed_payload:
604
+ index += consumed_payload.value
605
+
606
+ continue
607
+
608
+ if not end_of_options and arg.starts_with("-") and arg.len > 1:
609
+ if arg.equal("-h"):
610
+ result.show_help = true
611
+ return Result[Match, Error].success(value= result)
612
+
613
+ var next_arg = Option[str].none
614
+ if index + 1 < args.len:
615
+ next_arg = Option[str].some(value = unsafe: read(args.data + index + 1))
616
+
617
+ match parse_short_option_impl(ptr_of(result), app.options, command_options, arg, next_arg):
618
+ Result.failure as payload:
619
+ result.release()
620
+ return Result[Match, Error].failure(error= payload.error)
621
+ Result.success as consumed_payload:
622
+ index += consumed_payload.value
623
+
624
+ continue
625
+
626
+ if not result.has_command and app.commands.len != 0:
627
+ let command_ptr = find_command(app.commands, arg) else:
628
+ result.release()
629
+ return Result[Match, Error].failure(error= unknown_command_error(arg))
630
+
631
+ let command = unsafe: read(command_ptr)
632
+ result.has_command = true
633
+ result.command_name.assign(command.name)
634
+ command_options = command.options
635
+ match append_option_specs(ptr_of(result.options), command.options):
636
+ Result.failure as append_error_payload:
637
+ result.release()
638
+ return Result[Match, Error].failure(error= append_error_payload.error)
639
+ Result.success:
640
+ pass
641
+ index += 1
642
+ continue
643
+
644
+ result.positionals.push(string.String.from_str(arg))
645
+ index += 1
646
+
647
+ match validate_required_options(result, app.options, command_options):
648
+ Result.failure as validate_payload:
649
+ result.release()
650
+ return Result[Match, Error].failure(error= validate_payload.error)
651
+ Result.success:
652
+ pass
653
+
654
+ return Result[Match, Error].success(value= result)
655
+
656
+
657
+ public function render_help(app: AppSpec) -> string.String:
658
+ var output = string.String.create()
659
+ var title = fmt.format(f"#{app.name} - #{app.summary}")
660
+ var usage = usage_for_app(app)
661
+ append_help_header(ref_of(output), title.as_str(), app.summary, usage.as_str())
662
+ append_commands_section(ref_of(output), app.commands)
663
+ append_options_section(ref_of(output), app.options, zero[span[OptionSpec]])
664
+ title.release()
665
+ usage.release()
666
+ return output
667
+
668
+
669
+ public function render_command_help(app: AppSpec, command_name: str) -> Result[string.String, Error]:
670
+ let command_ptr = find_command(app.commands, command_name) else:
671
+ return Result[string.String, Error].failure(error= unknown_command_error(command_name))
672
+
673
+ let command = unsafe: read(command_ptr)
674
+ var output = string.String.create()
675
+ var title = fmt.format(f"#{app.name} #{command.name} - #{command.help}")
676
+ var usage = usage_for_command(app, command)
677
+ append_help_header(ref_of(output), title.as_str(), command.help, usage.as_str())
678
+ append_options_section(ref_of(output), app.options, command.options)
679
+ title.release()
680
+ usage.release()
681
+ return Result[string.String, Error].success(value= output)
682
+
683
+
684
+ extending OptionMatch:
685
+ public editable function release() -> void:
686
+ this.name.release()
687
+ this.value.release()
688
+
689
+
690
+ extending Match:
691
+ public function command() -> Option[str]:
692
+ if not this.has_command:
693
+ return Option[str].none
694
+
695
+ return Option[str].some(value= this.command_name.as_str())
696
+
697
+
698
+ public function help_requested() -> bool:
699
+ return this.show_help
700
+
701
+
702
+ public function supplied(name: str) -> bool:
703
+ let option_match = find_option_match(this.options, name) else:
704
+ return false
705
+
706
+ return unsafe: read(option_match).present
707
+
708
+
709
+ public function flag(name: str) -> bool:
710
+ return this.supplied(name)
711
+
712
+
713
+ public function value(name: str) -> Option[str]:
714
+ let option_match = find_option_match(this.options, name) else:
715
+ return Option[str].none
716
+
717
+ unsafe:
718
+ if not read(option_match).has_value:
719
+ return Option[str].none
720
+
721
+ return Option[str].some(value= read(option_match).value.as_str())
722
+
723
+
724
+ public function positionals_len() -> ptr_uint:
725
+ return this.positionals.len()
726
+
727
+
728
+ public function positional(index: ptr_uint) -> Option[str]:
729
+ let value = this.positionals.get(index) else:
730
+ return Option[str].none
731
+
732
+ unsafe:
733
+ return Option[str].some(value= read(value).as_str())
734
+
735
+
736
+ public editable function release() -> void:
737
+ this.command_name.release()
738
+
739
+ var option_index: ptr_uint = 0
740
+ while option_index < this.options.len():
741
+ let value = this.options.get(option_index)
742
+ if value == null:
743
+ break
744
+
745
+ unsafe:
746
+ read(ptr[OptionMatch]<-value).release()
747
+
748
+ option_index += 1
749
+
750
+ this.options.release()
751
+
752
+ var positional_index: ptr_uint = 0
753
+ while positional_index < this.positionals.len():
754
+ let value = this.positionals.get(positional_index)
755
+ if value == null:
756
+ break
757
+
758
+ unsafe:
759
+ read(ptr[string.String]<-value).release()
760
+
761
+ positional_index += 1
762
+
763
+ this.positionals.release()
764
+
765
+
766
+ extending Error:
767
+ public editable function release() -> void:
768
+ this.message.release()