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/tracy.mt ADDED
@@ -0,0 +1,42 @@
1
+ # generated by mtc imported-bindings from std.c.tracy using bindings/imported/tracy.binding.json
2
+
3
+ import std.c.tracy as c
4
+
5
+ public type Zone = c.TracyCZoneCtx
6
+ public type SourceLocation = c.TracyCSourceLocation
7
+ public type GpuTime = c.TracyCGpuTime
8
+ public type GpuZoneName = c.TracyCGpuZoneName
9
+
10
+ public foreign function zone_begin(srcloc: const_ptr[SourceLocation]?, active: int) -> Zone = c.tracy_emit_zone_begin
11
+ public foreign function emit_zone_begin_callstack(srcloc: const_ptr[SourceLocation]?, depth: int, active: int) -> Zone = c.tracy_emit_zone_begin_callstack
12
+ public foreign function zone_end(zone: Zone) -> void = c.tracy_emit_zone_end
13
+ public foreign function zone_text(zone: const_ptr[Zone], text: str as cstr, size: ptr_uint) -> void = c.tracy_emit_zone_text
14
+ public foreign function zone_name(zone: const_ptr[Zone], name: str as cstr, size: ptr_uint) -> void = c.tracy_emit_zone_name
15
+ public foreign function emit_zone_color(ctx: Zone, color: uint) -> void = c.tracy_emit_zone_color
16
+ public foreign function zone_value(zone: const_ptr[Zone], value: long) -> void = c.tracy_emit_zone_value
17
+ public foreign function emit_gpu_zone_begin(srcloc: const_ptr[SourceLocation]?, active: int) -> Zone = c.tracy_emit_gpu_zone_begin
18
+ public foreign function emit_gpu_zone_begin_callstack(srcloc: const_ptr[SourceLocation]?, depth: int, active: int) -> Zone = c.tracy_emit_gpu_zone_begin_callstack
19
+ public foreign function emit_gpu_zone_end(ctx: Zone) -> void = c.tracy_emit_gpu_zone_end
20
+ public foreign function emit_gpu_zone_name(ctx: Zone, name: str as cstr, size: ptr_uint) -> void = c.tracy_emit_gpu_zone_name
21
+ public foreign function emit_gpu_zone_value(ctx: Zone, value: long) -> void = c.tracy_emit_gpu_zone_value
22
+ public foreign function emit_gpu_new_context(ctx: Zone) -> void = c.tracy_emit_gpu_new_context
23
+ public foreign function emit_gpu_context_name(ctx: Zone, name: str as cstr, size: ptr_uint) -> void = c.tracy_emit_gpu_context_name
24
+ public foreign function emit_gpu_calibration(ctx: Zone) -> void = c.tracy_emit_gpu_calibration
25
+ public foreign function emit_gpu_time(data: const_ptr[GpuTime]) -> void = c.tracy_emit_gpu_time
26
+ public foreign function emit_gpu_time_sync(data: const_ptr[GpuTime]) -> void = c.tracy_emit_gpu_time_sync
27
+ public foreign function frame_mark(name: str as cstr) -> void = c.tracy_emit_frame_mark
28
+ public foreign function frame_mark_start(name: str as cstr) -> void = c.tracy_emit_frame_mark_start
29
+ public foreign function frame_mark_end(name: str as cstr) -> void = c.tracy_emit_frame_mark_end
30
+ public foreign function plot(name: str as cstr, value: double) -> void = c.tracy_emit_plot
31
+ public foreign function emit_plot_config(name: str as cstr, format_type: int, step: int, fill: int, color: uint) -> void = c.tracy_emit_plot_config
32
+ public foreign function plot_float(name: str as cstr, value: float) -> void = c.tracy_emit_plot_float
33
+ public foreign function plot_int(name: str as cstr, value: long) -> void = c.tracy_emit_plot_int
34
+ public foreign function message(text: str as cstr, size: ptr_uint, callstack: int) -> void = c.tracy_emit_message
35
+ public foreign function emit_message_color(text: str as cstr, size: ptr_uint, color: uint, callstack: int) -> void = c.tracy_emit_message_color
36
+ public foreign function message_l(text: str as cstr, callstack: int) -> void = c.tracy_emit_message_l
37
+ public foreign function message_lc(text: str as cstr, color: uint, callstack: int) -> void = c.tracy_emit_message_lc
38
+ public foreign function is_connected() -> int = c.tracy_connected
39
+ public foreign function fiber_enter(fiber: str as cstr) -> void = c.tracy_fiber_enter
40
+ public foreign function fiber_leave() -> void = c.tracy_fiber_leave
41
+ public foreign function startup() -> void = c.tracy_startup_profiler
42
+ public foreign function shutdown() -> void = c.tracy_shutdown_profiler
data/std/uri.mt ADDED
@@ -0,0 +1,118 @@
1
+ import std.path as path
2
+ import std.str as text
3
+ import std.string as string
4
+
5
+ const FILE_URI_PREFIX: str = "file://"
6
+
7
+
8
+ public function file_uri_from_path(path_text: str) -> string.String:
9
+ var normalized = path.normalize_separators(path_text)
10
+ defer normalized.release()
11
+
12
+ var result = string.String.from_str(FILE_URI_PREFIX)
13
+ append_percent_encoded_path(ref_of(result), normalized.as_str())
14
+ return result
15
+
16
+
17
+ public function path_from_file_uri(uri: str) -> Option[string.String]:
18
+ if not uri.starts_with(FILE_URI_PREFIX):
19
+ return Option[string.String].none
20
+
21
+ let encoded_path = uri.slice(FILE_URI_PREFIX.len, uri.len - FILE_URI_PREFIX.len)
22
+ var decoded = percent_decode(encoded_path)?
23
+ match owned_utf8_view(decoded):
24
+ Option.none:
25
+ decoded.release()
26
+ return Option[string.String].none
27
+ Option.some as view_payload:
28
+ let decoded_path = view_payload.value
29
+ if leading_slash_drive_path(decoded_path):
30
+ let normalized = string.String.from_str(decoded_path.slice(1, decoded_path.len - 1))
31
+ decoded.release()
32
+ return Option[string.String].some(value= normalized)
33
+
34
+ return Option[string.String].some(value= decoded)
35
+
36
+
37
+ public function percent_decode(text_value: str) -> Option[string.String]:
38
+ var result = string.String.with_capacity(text_value.len)
39
+
40
+ var index: ptr_uint = 0
41
+ while index < text_value.len:
42
+ let value = text_value.byte_at(index)
43
+ if value != 37:
44
+ result.push_byte(value)
45
+ index += 1
46
+ continue
47
+
48
+ if index + 2 >= text_value.len:
49
+ result.release()
50
+ return Option[string.String].none
51
+
52
+ let high = hex_digit_value(text_value.byte_at(index + 1))
53
+ let low = hex_digit_value(text_value.byte_at(index + 2))
54
+ if high < 0 or low < 0:
55
+ result.release()
56
+ return Option[string.String].none
57
+
58
+ result.push_byte(ubyte<-(high * 16 + low))
59
+ index += 3
60
+
61
+ return Option[string.String].some(value= result)
62
+
63
+
64
+ function append_percent_encoded_path(output: ref[string.String], path_text: str) -> void:
65
+ var index: ptr_uint = 0
66
+ while index < path_text.len:
67
+ let value = path_text.byte_at(index)
68
+ if value == 47:
69
+ output.push_byte(value)
70
+ else if safe_path_byte(value):
71
+ output.push_byte(value)
72
+ else:
73
+ append_percent_encoded_byte(output, value)
74
+ index += 1
75
+
76
+
77
+ function append_percent_encoded_byte(output: ref[string.String], value: ubyte) -> void:
78
+ output.push_byte(37)
79
+ output.push_byte(hex_digit(value >> 4))
80
+ output.push_byte(hex_digit(value & 0x0F))
81
+
82
+
83
+ function hex_digit(value: ubyte) -> ubyte:
84
+ if value < 10:
85
+ return (48 + value)
86
+
87
+ return (65 + (value - 10))
88
+
89
+
90
+ function hex_digit_value(value: ubyte) -> int:
91
+ if value >= 48 and value <= 57:
92
+ return (value - 48ub)
93
+ if value >= 65 and value <= 70:
94
+ return 10 + int<-(value - 65ub)
95
+ if value >= 97 and value <= 102:
96
+ return 10 + int<-(value - 97ub)
97
+
98
+ return -1
99
+
100
+
101
+ function safe_path_byte(value: ubyte) -> bool:
102
+ return ascii_letter(value) or ascii_digit(value) or value == 95 or value == 46 or value == 45 or value == 42
103
+
104
+
105
+ function ascii_letter(value: ubyte) -> bool:
106
+ return (value >= 65 and value <= 90) or (value >= 97 and value <= 122)
107
+
108
+
109
+ function ascii_digit(value: ubyte) -> bool:
110
+ return value >= 48 and value <= 57
111
+
112
+
113
+ function leading_slash_drive_path(path_text: str) -> bool:
114
+ return path_text.len >= 3 and path_text.byte_at(0) == 47 and ascii_letter(path_text.byte_at(1)) and path_text.byte_at(2) == 58
115
+
116
+
117
+ function owned_utf8_view(value: string.String) -> Option[str]:
118
+ return text.utf8_byte_span_as_str(unsafe: span[ubyte](data = ptr[ubyte]<-value.data, len = value.len))
data/std/url.mt ADDED
@@ -0,0 +1,372 @@
1
+ import std.str as text
2
+ import std.string as string
3
+ import std.vec as vec
4
+
5
+
6
+ function is_unreserved(value: ubyte) -> bool:
7
+ return (
8
+ (value >= 48 and value <= 57)
9
+ or (value >= 65 and value <= 90)
10
+ or (value >= 97 and value <= 122)
11
+ or value == 45
12
+ or value == 46
13
+ or value == 95
14
+ or value == 126
15
+ )
16
+
17
+
18
+ function hex_digit(value: ubyte) -> ubyte:
19
+ if value < 10:
20
+ return 48 + value
21
+
22
+ return 65 + (value - 10)
23
+
24
+
25
+ function hex_value(value: ubyte) -> int:
26
+ if value >= 48 and value <= 57:
27
+ return (value - 48ub)
28
+ if value >= 65 and value <= 70:
29
+ return 10 + int<-(value - 65ub)
30
+ if value >= 97 and value <= 102:
31
+ return 10 + int<-(value - 97ub)
32
+
33
+ return -1
34
+
35
+
36
+ function find_byte_from(data: str, target: ubyte, start: ptr_uint) -> Option[ptr_uint]:
37
+ var index = start
38
+ while index < data.len:
39
+ if data.byte_at(index) == target:
40
+ return Option[ptr_uint].some(value = index)
41
+ index += 1
42
+
43
+ return Option[ptr_uint].none
44
+
45
+
46
+ public function percent_encode(text_value: str) -> string.String:
47
+ var result = string.String.with_capacity(text_value.len * 3)
48
+ var index: ptr_uint = 0
49
+ while index < text_value.len:
50
+ let value = text_value.byte_at(index)
51
+ if is_unreserved(value):
52
+ result.push_byte(value)
53
+ else:
54
+ result.push_byte(37)
55
+ result.push_byte(hex_digit(value >> 4))
56
+ result.push_byte(hex_digit(value & 0x0F))
57
+ index += 1
58
+
59
+ return result
60
+
61
+
62
+ public function percent_decode(text_value: str) -> Option[string.String]:
63
+ var result = string.String.with_capacity(text_value.len)
64
+ var index: ptr_uint = 0
65
+ while index < text_value.len:
66
+ let value = text_value.byte_at(index)
67
+ if value != 37:
68
+ result.push_byte(value)
69
+ index += 1
70
+ continue
71
+
72
+ if index + 2 >= text_value.len:
73
+ result.release()
74
+ return Option[string.String].none
75
+
76
+ let high = hex_value(text_value.byte_at(index + 1))
77
+ let low = hex_value(text_value.byte_at(index + 2))
78
+ if high < 0 or low < 0:
79
+ result.release()
80
+ return Option[string.String].none
81
+
82
+ let decoded = ubyte<-(high * 16 + low)
83
+ result.push_byte(decoded)
84
+ index += 3
85
+
86
+ return Option[string.String].some(value = result)
87
+
88
+ public struct QueryParam:
89
+ key: string.String
90
+ value: string.String
91
+
92
+
93
+ public function parse_query(url: str) -> Result[vec.Vec[QueryParam], string.String]:
94
+ var params = vec.Vec[QueryParam].create()
95
+
96
+ let question = url.find_byte(63)
97
+ var rest: str = zero[str]
98
+ match question:
99
+ Option.none:
100
+ return Result[vec.Vec[QueryParam], string.String].success(value = params)
101
+ Option.some as q:
102
+ rest = url.slice(q.value + 1, url.len - q.value - 1)
103
+
104
+ let fragment = rest.find_byte(35)
105
+ match fragment:
106
+ Option.none:
107
+ pass
108
+ Option.some as f:
109
+ rest = rest.slice(0, f.value)
110
+
111
+ if rest.len == 0:
112
+ return Result[vec.Vec[QueryParam], string.String].success(value = params)
113
+
114
+ var start: ptr_uint = 0
115
+ while start < rest.len:
116
+ let amp = find_byte_from(rest, 38, start)
117
+ match amp:
118
+ Option.none:
119
+ let pair_text = rest.slice(start, rest.len - start)
120
+ let pair_result = parse_query_pair(pair_text)
121
+ match pair_result:
122
+ Result.failure as payload:
123
+ release_params(ref_of(params))
124
+ return Result[
125
+ vec.Vec[QueryParam],
126
+ string.String
127
+ ].failure(error = payload.error)
128
+ Result.success as payload:
129
+ params.push(payload.value)
130
+
131
+ start = rest.len
132
+ Option.some as a:
133
+ let pair_text = rest.slice(start, a.value - start)
134
+ let pair_result = parse_query_pair(pair_text)
135
+ match pair_result:
136
+ Result.failure as payload:
137
+ release_params(ref_of(params))
138
+ return Result[
139
+ vec.Vec[QueryParam],
140
+ string.String
141
+ ].failure(error = payload.error)
142
+ Result.success as payload:
143
+ params.push(payload.value)
144
+
145
+ start = a.value + 1
146
+
147
+ return Result[vec.Vec[QueryParam], string.String].success(value = params)
148
+
149
+
150
+ function parse_query_pair(text_value: str) -> Result[QueryParam, string.String]:
151
+ let eq = text_value.find_byte(61)
152
+ match eq:
153
+ Option.none:
154
+ let decoded_key = percent_decode(text_value) else:
155
+ return Result[
156
+ QueryParam,
157
+ string.String
158
+ ].failure(error = string.String.from_str("url: invalid percent-encoding in query key"))
159
+
160
+ return Result[QueryParam, string.String].success(
161
+ value = QueryParam(key = decoded_key, value = string.String.create())
162
+ )
163
+ Option.some as e:
164
+ let key_text = text_value.slice(0, e.value)
165
+ let value_text = text_value.slice(e.value + 1, text_value.len - e.value - 1)
166
+
167
+ let decoded_key = percent_decode(key_text) else:
168
+ return Result[
169
+ QueryParam,
170
+ string.String
171
+ ].failure(error = string.String.from_str("url: invalid percent-encoding in query key"))
172
+
173
+ let decoded_value = percent_decode(value_text) else:
174
+ var owned_key = decoded_key
175
+ owned_key.release()
176
+ return Result[
177
+ QueryParam,
178
+ string.String
179
+ ].failure(error = string.String.from_str("url: invalid percent-encoding in query value"))
180
+
181
+ return Result[QueryParam, string.String].success(
182
+ value = QueryParam(key = decoded_key, value = decoded_value)
183
+ )
184
+
185
+
186
+ public function build_query(params: span[QueryParam]) -> string.String:
187
+ var result = string.String.create()
188
+
189
+ var index: ptr_uint = 0
190
+ while index < params.len:
191
+ if index > 0:
192
+ result.push_byte(38)
193
+
194
+ let param = unsafe: read(params.data + index)
195
+ var encoded_key = percent_encode(param.key.as_str())
196
+ var encoded_value = percent_encode(param.value.as_str())
197
+
198
+ result.append(encoded_key.as_str())
199
+ result.push_byte(61)
200
+ result.append(encoded_value.as_str())
201
+
202
+ encoded_key.release()
203
+ encoded_value.release()
204
+
205
+ index += 1
206
+
207
+ return result
208
+
209
+ public struct FormField:
210
+ key: str
211
+ value: str
212
+
213
+
214
+ public function encode_form(fields: span[FormField]) -> string.String:
215
+ var result = string.String.create()
216
+
217
+ var index: ptr_uint = 0
218
+ while index < fields.len:
219
+ if index > 0:
220
+ result.push_byte(38)
221
+
222
+ let field = unsafe: read(fields.data + index)
223
+ append_form_encoded(ref_of(result), field.key)
224
+ result.push_byte(61)
225
+ append_form_encoded(ref_of(result), field.value)
226
+
227
+ index += 1
228
+
229
+ return result
230
+
231
+
232
+ function append_form_encoded(output: ref[string.String], text_value: str) -> void:
233
+ var index: ptr_uint = 0
234
+ while index < text_value.len:
235
+ let value = text_value.byte_at(index)
236
+ if value == 32:
237
+ output.push_byte(43)
238
+ else if is_unreserved(value):
239
+ output.push_byte(value)
240
+ else:
241
+ output.push_byte(37)
242
+ output.push_byte(hex_digit(value >> 4))
243
+ output.push_byte(hex_digit(value & 0x0F))
244
+ index += 1
245
+
246
+
247
+ public function decode_form(body: str) -> Result[vec.Vec[QueryParam], string.String]:
248
+ var params = vec.Vec[QueryParam].create()
249
+
250
+ if body.len == 0:
251
+ return Result[vec.Vec[QueryParam], string.String].success(value = params)
252
+
253
+ var start: ptr_uint = 0
254
+ while start < body.len:
255
+ let amp = find_byte_from(body, 38, start)
256
+ match amp:
257
+ Option.none:
258
+ let pair_text = body.slice(start, body.len - start)
259
+ let pair_result = parse_form_pair(pair_text)
260
+ match pair_result:
261
+ Result.failure as payload:
262
+ release_params(ref_of(params))
263
+ return Result[
264
+ vec.Vec[QueryParam],
265
+ string.String
266
+ ].failure(error = payload.error)
267
+ Result.success as payload:
268
+ params.push(payload.value)
269
+
270
+ start = body.len
271
+ Option.some as a:
272
+ let pair_text = body.slice(start, a.value - start)
273
+ let pair_result = parse_form_pair(pair_text)
274
+ match pair_result:
275
+ Result.failure as payload:
276
+ release_params(ref_of(params))
277
+ return Result[
278
+ vec.Vec[QueryParam],
279
+ string.String
280
+ ].failure(error = payload.error)
281
+ Result.success as payload:
282
+ params.push(payload.value)
283
+
284
+ start = a.value + 1
285
+
286
+ return Result[vec.Vec[QueryParam], string.String].success(value = params)
287
+
288
+
289
+ function parse_form_pair(text_value: str) -> Result[QueryParam, string.String]:
290
+ let eq = text_value.find_byte(61)
291
+ match eq:
292
+ Option.none:
293
+ let decoded_key = form_percent_decode(text_value) else:
294
+ return Result[
295
+ QueryParam,
296
+ string.String
297
+ ].failure(error = string.String.from_str("url: invalid percent-encoding in form key"))
298
+
299
+ return Result[QueryParam, string.String].success(
300
+ value = QueryParam(key = decoded_key, value = string.String.create())
301
+ )
302
+ Option.some as e:
303
+ let key_text = text_value.slice(0, e.value)
304
+ let value_text = text_value.slice(e.value + 1, text_value.len - e.value - 1)
305
+
306
+ let decoded_key = form_percent_decode(key_text) else:
307
+ return Result[
308
+ QueryParam,
309
+ string.String
310
+ ].failure(error = string.String.from_str("url: invalid percent-encoding in form key"))
311
+
312
+ let decoded_value = form_percent_decode(value_text) else:
313
+ var owned_key = decoded_key
314
+ owned_key.release()
315
+ return Result[
316
+ QueryParam,
317
+ string.String
318
+ ].failure(error = string.String.from_str("url: invalid percent-encoding in form value"))
319
+
320
+ return Result[QueryParam, string.String].success(
321
+ value = QueryParam(key = decoded_key, value = decoded_value)
322
+ )
323
+
324
+
325
+ function form_percent_decode(text_value: str) -> Option[string.String]:
326
+ var result = string.String.with_capacity(text_value.len)
327
+ var index: ptr_uint = 0
328
+ while index < text_value.len:
329
+ let value = text_value.byte_at(index)
330
+ if value == 43:
331
+ result.push_byte(32)
332
+ index += 1
333
+ else if value != 37:
334
+ result.push_byte(value)
335
+ index += 1
336
+ else:
337
+ if index + 2 >= text_value.len:
338
+ result.release()
339
+ return Option[string.String].none
340
+
341
+ let high = hex_value(text_value.byte_at(index + 1))
342
+ let low = hex_value(text_value.byte_at(index + 2))
343
+ if high < 0 or low < 0:
344
+ result.release()
345
+ return Option[string.String].none
346
+
347
+ let decoded = ubyte<-(high * 16 + low)
348
+ result.push_byte(decoded)
349
+ index += 3
350
+
351
+ return Option[string.String].some(value = result)
352
+
353
+
354
+ function release_params(params: ref[vec.Vec[QueryParam]]) -> void:
355
+ var index: ptr_uint = 0
356
+ while index < params.len():
357
+ let current = params.get(index) else:
358
+ fatal(c"url.release_params missing value")
359
+
360
+ unsafe:
361
+ var param = read(current)
362
+ param.release()
363
+
364
+ index += 1
365
+
366
+ params.release()
367
+
368
+
369
+ extending QueryParam:
370
+ public editable function release() -> void:
371
+ this.key.release()
372
+ this.value.release()