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.
- checksums.yaml +4 -4
- data/docs/index.html +4 -3
- data/lib/milk_tea/base.rb +1 -1
- data/std/asset_pack.mt +267 -0
- data/std/async/libuv_runtime.mt +539 -0
- data/std/async/mailbox.mt +191 -0
- data/std/async/runtime.mt +87 -0
- data/std/async.mt +87 -0
- data/std/base64.mt +185 -0
- data/std/behavior_tree.mt +396 -0
- data/std/binary.mt +314 -0
- data/std/binary_heap.mt +157 -0
- data/std/bitset.mt +239 -0
- data/std/box2d.mt +535 -0
- data/std/bytes.mt +46 -0
- data/std/c/box2d.mt +1254 -0
- data/std/c/cgltf.mt +647 -0
- data/std/c/cjson.mt +106 -0
- data/std/c/crypto.mt +11 -0
- data/std/c/crypto_support.h +51 -0
- data/std/c/ctype.mt +19 -0
- data/std/c/ctype_bindgen.h +29 -0
- data/std/c/curl.mt +1223 -0
- data/std/c/enet.mt +473 -0
- data/std/c/errno.mt +41 -0
- data/std/c/errno_bindgen.h +53 -0
- data/std/c/flecs.mt +2945 -0
- data/std/c/fs.linux.mt +43 -0
- data/std/c/fs.windows.mt +43 -0
- data/std/c/fs_support.h +954 -0
- data/std/c/gl.mt +2065 -0
- data/std/c/gl_registry_helpers.h +10748 -0
- data/std/c/glfw.mt +499 -0
- data/std/c/libc.mt +167 -0
- data/std/c/libuv.mt +1491 -0
- data/std/c/math.mt +22 -0
- data/std/c/math_bindgen.h +31 -0
- data/std/c/miniaudio.mt +3420 -0
- data/std/c/pcre2.mt +735 -0
- data/std/c/process.mt +58 -0
- data/std/c/process_support.h +1147 -0
- data/std/c/raygui.mt +1602 -0
- data/std/c/raylib.mt +1245 -0
- data/std/c/raymath.mt +163 -0
- data/std/c/rlgl.mt +434 -0
- data/std/c/rpng.mt +60 -0
- data/std/c/rres.mt +187 -0
- data/std/c/sdl3.mt +4132 -0
- data/std/c/sqlite3.mt +951 -0
- data/std/c/stb_image.mt +52 -0
- data/std/c/stb_image_resize2.mt +128 -0
- data/std/c/stb_image_write.mt +19 -0
- data/std/c/stb_rect_pack.mt +38 -0
- data/std/c/stb_truetype.mt +175 -0
- data/std/c/stb_vorbis.mt +31 -0
- data/std/c/stdio.mt +90 -0
- data/std/c/steamworks.h +5769 -0
- data/std/c/steamworks.mt +4771 -0
- data/std/c/string.mt +16 -0
- data/std/c/string_bindgen.h +36 -0
- data/std/c/sync.mt +33 -0
- data/std/c/sync_support.h +219 -0
- data/std/c/terminal.mt +24 -0
- data/std/c/terminal_support.h +408 -0
- data/std/c/time.mt +26 -0
- data/std/c/tls.mt +24 -0
- data/std/c/tls_support.h +626 -0
- data/std/c/tracy.mt +53 -0
- data/std/c/zlib.mt +20 -0
- data/std/c/zlib_support.h +239 -0
- data/std/c/zstd.mt +231 -0
- data/std/cell.mt +60 -0
- data/std/cgltf.mt +115 -0
- data/std/cjson.mt +90 -0
- data/std/cli.mt +768 -0
- data/std/cookie.mt +198 -0
- data/std/counter.mt +160 -0
- data/std/crypto.mt +74 -0
- data/std/cstring.mt +13 -0
- data/std/ctype.mt +52 -0
- data/std/curl/runtime.mt +261 -0
- data/std/curl.mt +38 -0
- data/std/deque.mt +348 -0
- data/std/encoding.mt +93 -0
- data/std/enet.mt +133 -0
- data/std/env.mt +41 -0
- data/std/errno.mt +47 -0
- data/std/flecs.mt +978 -0
- data/std/fmt.mt +271 -0
- data/std/fs.linux.mt +696 -0
- data/std/fs.windows.mt +711 -0
- data/std/fsm.mt +267 -0
- data/std/gl.mt +2062 -0
- data/std/glfw.mt +471 -0
- data/std/goap.mt +342 -0
- data/std/graph.mt +521 -0
- data/std/gzip.mt +77 -0
- data/std/hash.mt +429 -0
- data/std/http/server.mt +810 -0
- data/std/http.mt +1227 -0
- data/std/intern.mt +53 -0
- data/std/jobs.mt +288 -0
- data/std/json.mt +627 -0
- data/std/libc.mt +34 -0
- data/std/libuv.mt +554 -0
- data/std/linear_algebra.mt +243 -0
- data/std/linked_map.mt +372 -0
- data/std/linked_map_view.mt +49 -0
- data/std/linked_set.mt +117 -0
- data/std/log.mt +69 -0
- data/std/map.mt +418 -0
- data/std/math.mt +24 -0
- data/std/mem/arena.mt +137 -0
- data/std/mem/endian.mt +51 -0
- data/std/mem/heap.mt +298 -0
- data/std/mem/pool.mt +164 -0
- data/std/mem/stack.mt +47 -0
- data/std/mem/tracking.mt +119 -0
- data/std/miniaudio.mt +1299 -0
- data/std/multiset.mt +191 -0
- data/std/net/channel.mt +727 -0
- data/std/net/clock.mt +237 -0
- data/std/net/discovery.mt +277 -0
- data/std/net/lobby.mt +813 -0
- data/std/net/manager.mt +539 -0
- data/std/net/mux.mt +839 -0
- data/std/net/nat.mt +122 -0
- data/std/net/packet.mt +219 -0
- data/std/net/punch.mt +162 -0
- data/std/net/rpc.mt +119 -0
- data/std/net/session.mt +1157 -0
- data/std/net/stun.mt +252 -0
- data/std/net/sync.mt +183 -0
- data/std/net/turn.mt +459 -0
- data/std/net.mt +2944 -0
- data/std/oauth2.mt +408 -0
- data/std/option.mt +51 -0
- data/std/ordered_map.mt +497 -0
- data/std/ordered_set.mt +348 -0
- data/std/path.mt +368 -0
- data/std/pcre2/runtime.mt +35 -0
- data/std/pcre2.mt +78 -0
- data/std/priority_queue.mt +55 -0
- data/std/process.mt +837 -0
- data/std/queue.mt +55 -0
- data/std/random.mt +146 -0
- data/std/raygui.mt +99 -0
- data/std/raylib/debug_console.mt +71 -0
- data/std/raylib/easing.mt +388 -0
- data/std/raylib/packed_assets.mt +255 -0
- data/std/raylib/runtime.mt +30 -0
- data/std/raylib/tracy_gpu.mt +37 -0
- data/std/raylib.mt +1510 -0
- data/std/raymath.mt +153 -0
- data/std/result.mt +89 -0
- data/std/rlgl.mt +268 -0
- data/std/rpng.mt +46 -0
- data/std/rres.mt +36 -0
- data/std/sdl3/runtime.mt +54 -0
- data/std/sdl3.mt +1731 -0
- data/std/serialize.mt +68 -0
- data/std/set.mt +124 -0
- data/std/spatial.mt +176 -0
- data/std/sqlite3.mt +151 -0
- data/std/stack.mt +55 -0
- data/std/stb_image.mt +47 -0
- data/std/stb_image_resize2.mt +41 -0
- data/std/stb_image_write.mt +17 -0
- data/std/stb_rect_pack.mt +15 -0
- data/std/stb_truetype.mt +77 -0
- data/std/stb_vorbis.mt +16 -0
- data/std/stdio.mt +88 -0
- data/std/steamworks.mt +1542 -0
- data/std/str.mt +293 -0
- data/std/string.mt +234 -0
- data/std/sync.mt +194 -0
- data/std/tar.mt +704 -0
- data/std/terminal.mt +1002 -0
- data/std/testing.mt +266 -0
- data/std/thread.mt +120 -0
- data/std/time.mt +105 -0
- data/std/tls.mt +616 -0
- data/std/toml.mt +1310 -0
- data/std/tracy.mt +42 -0
- data/std/uri.mt +118 -0
- data/std/url.mt +372 -0
- data/std/vec.mt +433 -0
- data/std/zstd.mt +94 -0
- metadata +187 -2
data/std/fs.windows.mt
ADDED
|
@@ -0,0 +1,711 @@
|
|
|
1
|
+
import std.c.fs as c
|
|
2
|
+
import std.bytes as bytes
|
|
3
|
+
import std.libc as libc
|
|
4
|
+
import std.mem.arena as arena
|
|
5
|
+
import std.mem.heap as heap
|
|
6
|
+
import std.path as path_ops
|
|
7
|
+
import std.str as text
|
|
8
|
+
import std.string as string
|
|
9
|
+
import std.vec as vec
|
|
10
|
+
|
|
11
|
+
const path_kind_none: int = 0
|
|
12
|
+
const path_kind_file: int = 1
|
|
13
|
+
const path_kind_directory: int = 2
|
|
14
|
+
const path_kind_other: int = 3
|
|
15
|
+
public struct Error:
|
|
16
|
+
code: int
|
|
17
|
+
message: string.String
|
|
18
|
+
|
|
19
|
+
public struct Entries:
|
|
20
|
+
values: vec.Vec[string.String]
|
|
21
|
+
|
|
22
|
+
public enum MetadataKind: int
|
|
23
|
+
none = 0
|
|
24
|
+
file = 1
|
|
25
|
+
directory = 2
|
|
26
|
+
other = 3
|
|
27
|
+
|
|
28
|
+
public struct Metadata:
|
|
29
|
+
kind: MetadataKind
|
|
30
|
+
mode: int
|
|
31
|
+
size: ptr_uint
|
|
32
|
+
modified_seconds: ptr_int
|
|
33
|
+
modified_nanoseconds: ptr_int
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
function take_owned_string(data: ptr[char]?, len: ptr_uint) -> string.String:
|
|
37
|
+
if data == null:
|
|
38
|
+
if len != 0:
|
|
39
|
+
fatal(c"fs.take_owned_string missing storage")
|
|
40
|
+
|
|
41
|
+
return string.String.create()
|
|
42
|
+
|
|
43
|
+
return unsafe: string.String(data = ptr[ubyte]<-data, len = len, capacity = len, owns_storage = true)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
function take_owned_bytes(data: ptr[char]?, len: ptr_uint) -> bytes.Bytes:
|
|
47
|
+
if data == null:
|
|
48
|
+
if len != 0:
|
|
49
|
+
fatal(c"fs.take_owned_bytes missing storage")
|
|
50
|
+
|
|
51
|
+
return bytes.Bytes.empty()
|
|
52
|
+
|
|
53
|
+
return unsafe: bytes.Bytes(data = own[ubyte]<-ptr[ubyte]<-data, len = len)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
function take_error(raw: c.mt_fs_error, fallback: str) -> Error:
|
|
57
|
+
if raw.message_data == null and raw.message_len == 0:
|
|
58
|
+
return Error(code = raw.code, message = string.String.from_str(fallback))
|
|
59
|
+
|
|
60
|
+
return Error(code = raw.code, message = take_owned_string(raw.message_data, raw.message_len))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
function validate_utf8_string(text_value: string.String, error_message: str) -> Result[string.String, Error]:
|
|
64
|
+
match text.utf8_byte_span_as_str(unsafe: span[ubyte](data = ptr[ubyte]<-text_value.data, len = text_value.len)):
|
|
65
|
+
Option.some:
|
|
66
|
+
return Result[string.String, Error].success(value= text_value)
|
|
67
|
+
Option.none:
|
|
68
|
+
var owned = text_value
|
|
69
|
+
owned.release()
|
|
70
|
+
return Result[string.String, Error].failure(error= Error(
|
|
71
|
+
code = -1,
|
|
72
|
+
message = string.String.from_str(error_message)
|
|
73
|
+
))
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
function release_string_values(values: ref[vec.Vec[string.String]]) -> void:
|
|
77
|
+
var index: ptr_uint = 0
|
|
78
|
+
while index < values.len():
|
|
79
|
+
let value_ptr = values.get(index) else:
|
|
80
|
+
fatal(c"fs.release_string_values missing value")
|
|
81
|
+
|
|
82
|
+
unsafe:
|
|
83
|
+
read(value_ptr).release()
|
|
84
|
+
|
|
85
|
+
index += 1
|
|
86
|
+
|
|
87
|
+
values.release()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
function path_kind(path: str) -> int:
|
|
91
|
+
var storage = arena.create(path.len + 1)
|
|
92
|
+
defer storage.release()
|
|
93
|
+
return c.mt_fs_path_kind(storage.to_cstr(path))
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
function metadata_kind(raw_kind: int) -> MetadataKind:
|
|
97
|
+
if raw_kind == path_kind_file:
|
|
98
|
+
return MetadataKind.file
|
|
99
|
+
|
|
100
|
+
if raw_kind == path_kind_directory:
|
|
101
|
+
return MetadataKind.directory
|
|
102
|
+
|
|
103
|
+
if raw_kind == path_kind_other:
|
|
104
|
+
return MetadataKind.other
|
|
105
|
+
|
|
106
|
+
return MetadataKind.none
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
extending Error:
|
|
110
|
+
public editable function release() -> void:
|
|
111
|
+
this.message.release()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
extending Entries:
|
|
115
|
+
public function len() -> ptr_uint:
|
|
116
|
+
return this.values.len()
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
public function get(index: ptr_uint) -> Option[str]:
|
|
120
|
+
let value_ptr = this.values.get(index) else:
|
|
121
|
+
return Option[str].none
|
|
122
|
+
|
|
123
|
+
unsafe:
|
|
124
|
+
return Option[str].some(value= read(value_ptr).as_str())
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
public function contains(name: str) -> bool:
|
|
128
|
+
var index: ptr_uint = 0
|
|
129
|
+
while index < this.values.len():
|
|
130
|
+
let value_ptr = this.values.get(index) else:
|
|
131
|
+
fatal(c"fs.Entries.contains missing value")
|
|
132
|
+
|
|
133
|
+
unsafe:
|
|
134
|
+
if read(value_ptr).as_str().equal(name):
|
|
135
|
+
return true
|
|
136
|
+
|
|
137
|
+
index += 1
|
|
138
|
+
|
|
139
|
+
return false
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
public editable function release() -> void:
|
|
143
|
+
release_string_values(ref_of(this.values))
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
extending Metadata:
|
|
147
|
+
public function is_file() -> bool:
|
|
148
|
+
return this.kind == MetadataKind.file
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
public function is_directory() -> bool:
|
|
152
|
+
return this.kind == MetadataKind.directory
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
public function is_other() -> bool:
|
|
156
|
+
return this.kind == MetadataKind.other
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
public function exists(path: str) -> bool:
|
|
160
|
+
return path_kind(path) != path_kind_none
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
public function is_file(path: str) -> bool:
|
|
164
|
+
return path_kind(path) == path_kind_file
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
public function is_directory(path: str) -> bool:
|
|
168
|
+
return path_kind(path) == path_kind_directory
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
public function read_text(path: str) -> Result[string.String, Error]:
|
|
172
|
+
var storage = arena.create(path.len + 1)
|
|
173
|
+
defer storage.release()
|
|
174
|
+
|
|
175
|
+
var raw_text = zero[c.mt_fs_string]
|
|
176
|
+
var raw_error = zero[c.mt_fs_error]
|
|
177
|
+
let status_code = c.mt_fs_read_text(storage.to_cstr(path), raw_text, raw_error)
|
|
178
|
+
if status_code != 0:
|
|
179
|
+
return Result[string.String, Error].failure(error= take_error(raw_error, "fs read failed"))
|
|
180
|
+
|
|
181
|
+
return validate_utf8_string(take_owned_string(raw_text.data, raw_text.len), "fs.read_text requires UTF-8 text")
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
public function read_lines(path: str) -> Result[Entries, Error]:
|
|
185
|
+
match read_text(path):
|
|
186
|
+
Result.failure as payload:
|
|
187
|
+
return Result[Entries, Error].failure(error= payload.error)
|
|
188
|
+
Result.success as payload:
|
|
189
|
+
var content = payload.value
|
|
190
|
+
defer content.release()
|
|
191
|
+
|
|
192
|
+
var values = vec.Vec[string.String].create()
|
|
193
|
+
let content_text = content.as_str()
|
|
194
|
+
var start: ptr_uint = 0
|
|
195
|
+
var index: ptr_uint = 0
|
|
196
|
+
while index < content_text.len:
|
|
197
|
+
if content_text.byte_at(index) == 10:
|
|
198
|
+
var stop = index
|
|
199
|
+
if stop > start and content_text.byte_at(stop - 1) == 13:
|
|
200
|
+
stop -= 1
|
|
201
|
+
|
|
202
|
+
values.push(string.String.from_str(content_text.slice(start, stop - start)))
|
|
203
|
+
start = index + 1
|
|
204
|
+
|
|
205
|
+
index += 1
|
|
206
|
+
|
|
207
|
+
if start < content_text.len:
|
|
208
|
+
var stop = content_text.len
|
|
209
|
+
if stop > start and content_text.byte_at(stop - 1) == 13:
|
|
210
|
+
stop -= 1
|
|
211
|
+
|
|
212
|
+
values.push(string.String.from_str(content_text.slice(start, stop - start)))
|
|
213
|
+
|
|
214
|
+
return Result[Entries, Error].success(value= Entries(values = values))
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
public function read_bytes(path: str) -> Result[bytes.Bytes, Error]:
|
|
218
|
+
var storage = arena.create(path.len + 1)
|
|
219
|
+
defer storage.release()
|
|
220
|
+
|
|
221
|
+
var raw_bytes = zero[c.mt_fs_string]
|
|
222
|
+
var raw_error = zero[c.mt_fs_error]
|
|
223
|
+
let status_code = c.mt_fs_read_bytes(storage.to_cstr(path), raw_bytes, raw_error)
|
|
224
|
+
if status_code != 0:
|
|
225
|
+
return Result[bytes.Bytes, Error].failure(error= take_error(raw_error, "fs read bytes failed"))
|
|
226
|
+
|
|
227
|
+
return Result[bytes.Bytes, Error].success(value= take_owned_bytes(raw_bytes.data, raw_bytes.len))
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
public function metadata(path: str) -> Result[Metadata, Error]:
|
|
231
|
+
var storage = arena.create(path.len + 1)
|
|
232
|
+
defer storage.release()
|
|
233
|
+
|
|
234
|
+
var raw_metadata = zero[c.mt_fs_metadata]
|
|
235
|
+
var raw_error = zero[c.mt_fs_error]
|
|
236
|
+
let status_code = c.mt_fs_get_metadata(storage.to_cstr(path), raw_metadata, raw_error)
|
|
237
|
+
if status_code != 0:
|
|
238
|
+
return Result[Metadata, Error].failure(error= take_error(raw_error, "fs metadata failed"))
|
|
239
|
+
|
|
240
|
+
return Result[Metadata, Error].success(value= Metadata(
|
|
241
|
+
kind = metadata_kind(raw_metadata.kind),
|
|
242
|
+
mode = raw_metadata.mode,
|
|
243
|
+
size = raw_metadata.size,
|
|
244
|
+
modified_seconds = raw_metadata.modified_seconds,
|
|
245
|
+
modified_nanoseconds = raw_metadata.modified_nanoseconds
|
|
246
|
+
))
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
public function write_text(path: str, content: str) -> Result[bool, Error]:
|
|
250
|
+
var storage = arena.create(path.len + 1)
|
|
251
|
+
defer storage.release()
|
|
252
|
+
|
|
253
|
+
var raw_error = zero[c.mt_fs_error]
|
|
254
|
+
let status_code = c.mt_fs_write_text(storage.to_cstr(path), content.data, content.len, raw_error)
|
|
255
|
+
if status_code != 0:
|
|
256
|
+
return Result[bool, Error].failure(error= take_error(raw_error, "fs write failed"))
|
|
257
|
+
|
|
258
|
+
return Result[bool, Error].success(value= true)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
public function write_bytes(path: str, content: span[ubyte]) -> Result[bool, Error]:
|
|
262
|
+
var storage = arena.create(path.len + 1)
|
|
263
|
+
defer storage.release()
|
|
264
|
+
|
|
265
|
+
var raw_error = zero[c.mt_fs_error]
|
|
266
|
+
let status_code = c.mt_fs_write_bytes(storage.to_cstr(path), content.data, content.len, raw_error)
|
|
267
|
+
if status_code != 0:
|
|
268
|
+
return Result[bool, Error].failure(error= take_error(raw_error, "fs write bytes failed"))
|
|
269
|
+
|
|
270
|
+
return Result[bool, Error].success(value= true)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
public function set_permissions(path: str, mode: int) -> Result[bool, Error]:
|
|
274
|
+
var storage = arena.create(path.len + 1)
|
|
275
|
+
defer storage.release()
|
|
276
|
+
|
|
277
|
+
var raw_error = zero[c.mt_fs_error]
|
|
278
|
+
let status_code = c.mt_fs_set_permissions(storage.to_cstr(path), mode, raw_error)
|
|
279
|
+
if status_code != 0:
|
|
280
|
+
return Result[bool, Error].failure(error= take_error(raw_error, "fs set permissions failed"))
|
|
281
|
+
|
|
282
|
+
return Result[bool, Error].success(value= true)
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
public function create_directories(path: str) -> Result[bool, Error]:
|
|
286
|
+
var storage = arena.create(path.len + 1)
|
|
287
|
+
defer storage.release()
|
|
288
|
+
|
|
289
|
+
var raw_error = zero[c.mt_fs_error]
|
|
290
|
+
let status_code = c.mt_fs_create_directories(storage.to_cstr(path), raw_error)
|
|
291
|
+
if status_code != 0:
|
|
292
|
+
return Result[bool, Error].failure(error= take_error(raw_error, "fs create directories failed"))
|
|
293
|
+
|
|
294
|
+
return Result[bool, Error].success(value= true)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
public function copy_entry(source_path: str, target_path: str) -> Result[bool, Error]:
|
|
298
|
+
if is_file(source_path):
|
|
299
|
+
match create_directories(path_ops.dirname(target_path)):
|
|
300
|
+
Result.failure as payload:
|
|
301
|
+
return Result[bool, Error].failure(error= payload.error)
|
|
302
|
+
Result.success:
|
|
303
|
+
pass
|
|
304
|
+
|
|
305
|
+
match read_bytes(source_path):
|
|
306
|
+
Result.failure as payload:
|
|
307
|
+
return Result[bool, Error].failure(error= payload.error)
|
|
308
|
+
Result.success as payload:
|
|
309
|
+
var data = payload.value
|
|
310
|
+
defer data.release()
|
|
311
|
+
return write_bytes(target_path, data.as_span())
|
|
312
|
+
|
|
313
|
+
if is_directory(source_path):
|
|
314
|
+
match create_directories(target_path):
|
|
315
|
+
Result.failure as payload:
|
|
316
|
+
return Result[bool, Error].failure(error= payload.error)
|
|
317
|
+
Result.success:
|
|
318
|
+
pass
|
|
319
|
+
|
|
320
|
+
match list_entries(source_path):
|
|
321
|
+
Result.failure as payload:
|
|
322
|
+
return Result[bool, Error].failure(error= payload.error)
|
|
323
|
+
Result.success as payload:
|
|
324
|
+
var entries = payload.value
|
|
325
|
+
defer entries.release()
|
|
326
|
+
|
|
327
|
+
var index: ptr_uint = 0
|
|
328
|
+
while index < entries.len():
|
|
329
|
+
match entries.get(index):
|
|
330
|
+
Option.none:
|
|
331
|
+
return Result[
|
|
332
|
+
bool,
|
|
333
|
+
Error
|
|
334
|
+
].failure(error= static_error("fs copy entry missing source entry"))
|
|
335
|
+
Option.some as entry_payload:
|
|
336
|
+
var child_source = path_ops.join(source_path, entry_payload.value)
|
|
337
|
+
var child_target = path_ops.join(target_path, entry_payload.value)
|
|
338
|
+
match copy_entry(child_source.as_str(), child_target.as_str()):
|
|
339
|
+
Result.failure as child_payload:
|
|
340
|
+
child_source.release()
|
|
341
|
+
child_target.release()
|
|
342
|
+
return Result[bool, Error].failure(error= child_payload.error)
|
|
343
|
+
Result.success:
|
|
344
|
+
pass
|
|
345
|
+
child_source.release()
|
|
346
|
+
child_target.release()
|
|
347
|
+
index += 1
|
|
348
|
+
|
|
349
|
+
return Result[bool, Error].success(value= true)
|
|
350
|
+
|
|
351
|
+
if exists(source_path):
|
|
352
|
+
return Result[
|
|
353
|
+
bool,
|
|
354
|
+
Error
|
|
355
|
+
].failure(error= static_error("fs.copy_entry supports only regular files and directories"))
|
|
356
|
+
|
|
357
|
+
return Result[bool, Error].failure(error= static_error("fs.copy_entry source does not exist"))
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
public function remove(path: str) -> Result[bool, Error]:
|
|
361
|
+
var storage = arena.create(path.len + 1)
|
|
362
|
+
defer storage.release()
|
|
363
|
+
|
|
364
|
+
var raw_error = zero[c.mt_fs_error]
|
|
365
|
+
let status_code = c.mt_fs_remove(storage.to_cstr(path), raw_error)
|
|
366
|
+
if status_code != 0:
|
|
367
|
+
return Result[bool, Error].failure(error= take_error(raw_error, "fs remove failed"))
|
|
368
|
+
|
|
369
|
+
return Result[bool, Error].success(value= true)
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
public function remove_tree(path: str) -> Result[bool, Error]:
|
|
373
|
+
if is_directory(path):
|
|
374
|
+
match list_entries(path):
|
|
375
|
+
Result.failure as payload:
|
|
376
|
+
return Result[bool, Error].failure(error= payload.error)
|
|
377
|
+
Result.success as payload:
|
|
378
|
+
var entries = payload.value
|
|
379
|
+
defer entries.release()
|
|
380
|
+
|
|
381
|
+
var index: ptr_uint = 0
|
|
382
|
+
while index < entries.len():
|
|
383
|
+
match entries.get(index):
|
|
384
|
+
Option.none:
|
|
385
|
+
return Result[bool, Error].failure(error= static_error("fs remove tree missing entry"))
|
|
386
|
+
Option.some as entry_payload:
|
|
387
|
+
var child_path = path_ops.join(path, entry_payload.value)
|
|
388
|
+
match remove_tree(child_path.as_str()):
|
|
389
|
+
Result.failure as child_payload:
|
|
390
|
+
child_path.release()
|
|
391
|
+
return Result[bool, Error].failure(error= child_payload.error)
|
|
392
|
+
Result.success:
|
|
393
|
+
pass
|
|
394
|
+
child_path.release()
|
|
395
|
+
index += 1
|
|
396
|
+
|
|
397
|
+
return remove(path)
|
|
398
|
+
|
|
399
|
+
if exists(path):
|
|
400
|
+
return remove(path)
|
|
401
|
+
|
|
402
|
+
return Result[bool, Error].success(value= true)
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
public function rename(source_path: str, target_path: str) -> Result[bool, Error]:
|
|
406
|
+
var source_storage = arena.create(source_path.len + 1)
|
|
407
|
+
defer source_storage.release()
|
|
408
|
+
var target_storage = arena.create(target_path.len + 1)
|
|
409
|
+
defer target_storage.release()
|
|
410
|
+
|
|
411
|
+
var raw_error = zero[c.mt_fs_error]
|
|
412
|
+
let status_code = c.mt_fs_rename(
|
|
413
|
+
source_storage.to_cstr(source_path),
|
|
414
|
+
target_storage.to_cstr(target_path),
|
|
415
|
+
raw_error
|
|
416
|
+
)
|
|
417
|
+
if status_code != 0:
|
|
418
|
+
return Result[bool, Error].failure(error= take_error(raw_error, "fs rename failed"))
|
|
419
|
+
|
|
420
|
+
return Result[bool, Error].success(value= true)
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
public function current_directory() -> Result[string.String, Error]:
|
|
424
|
+
var raw_text = zero[c.mt_fs_string]
|
|
425
|
+
var raw_error = zero[c.mt_fs_error]
|
|
426
|
+
let status_code = c.mt_fs_current_directory(raw_text, raw_error)
|
|
427
|
+
if status_code != 0:
|
|
428
|
+
return Result[string.String, Error].failure(error= take_error(raw_error, "fs current directory failed"))
|
|
429
|
+
|
|
430
|
+
return validate_utf8_string(
|
|
431
|
+
take_owned_string(raw_text.data, raw_text.len),
|
|
432
|
+
"fs.current_directory requires UTF-8 text"
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
public function find_ancestor_containing(path: str, entry_name: str) -> Option[string.String]:
|
|
437
|
+
var current = path_ops.normalize_separators(path_ops.dirname(path))
|
|
438
|
+
if is_directory(path):
|
|
439
|
+
current.release()
|
|
440
|
+
current = path_ops.normalize_separators(path)
|
|
441
|
+
|
|
442
|
+
while true:
|
|
443
|
+
var candidate = path_ops.join(current.as_str(), entry_name)
|
|
444
|
+
let found = exists(candidate.as_str())
|
|
445
|
+
candidate.release()
|
|
446
|
+
if found:
|
|
447
|
+
return Option[string.String].some(value= current)
|
|
448
|
+
|
|
449
|
+
let parent_text = path_ops.dirname(current.as_str())
|
|
450
|
+
if parent_text == current.as_str():
|
|
451
|
+
current.release()
|
|
452
|
+
return Option[string.String].none
|
|
453
|
+
|
|
454
|
+
let parent = string.String.from_str(parent_text)
|
|
455
|
+
current.release()
|
|
456
|
+
current = parent
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
function static_error(message: str) -> Error:
|
|
460
|
+
return Error(code = -1, message = string.String.from_str(message))
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
public function temporary_directory() -> string.String:
|
|
464
|
+
var raw_text = zero[c.mt_fs_string]
|
|
465
|
+
var raw_error = zero[c.mt_fs_error]
|
|
466
|
+
let status_code = c.mt_fs_temporary_directory(raw_text, raw_error)
|
|
467
|
+
if status_code == 0:
|
|
468
|
+
match validate_utf8_string(
|
|
469
|
+
take_owned_string(raw_text.data, raw_text.len),
|
|
470
|
+
"fs.temporary_directory requires UTF-8 text"
|
|
471
|
+
):
|
|
472
|
+
Result.success as payload:
|
|
473
|
+
return payload.value
|
|
474
|
+
Result.failure as payload:
|
|
475
|
+
payload.error.release()
|
|
476
|
+
|
|
477
|
+
if raw_error.message_data != null or raw_error.message_len != 0:
|
|
478
|
+
var ignored_error = take_error(raw_error, "fs temporary directory failed")
|
|
479
|
+
ignored_error.release()
|
|
480
|
+
|
|
481
|
+
let configured_temp = libc.get_environment_variable("TEMP")
|
|
482
|
+
if configured_temp != null:
|
|
483
|
+
let configured_path = text.cstr_as_str(configured_temp)
|
|
484
|
+
if configured_path.len != 0:
|
|
485
|
+
return string.String.from_str(configured_path)
|
|
486
|
+
|
|
487
|
+
let configured_tmp = libc.get_environment_variable("TMP")
|
|
488
|
+
if configured_tmp != null:
|
|
489
|
+
let configured_path = text.cstr_as_str(configured_tmp)
|
|
490
|
+
if configured_path.len != 0:
|
|
491
|
+
return string.String.from_str(configured_path)
|
|
492
|
+
|
|
493
|
+
let configured_local_app_data = libc.get_environment_variable("LOCALAPPDATA")
|
|
494
|
+
if configured_local_app_data != null:
|
|
495
|
+
let configured_path = text.cstr_as_str(configured_local_app_data)
|
|
496
|
+
if configured_path.len != 0:
|
|
497
|
+
return string.String.from_str(configured_path)
|
|
498
|
+
|
|
499
|
+
return string.String.from_str("C:/Windows/Temp")
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
public function canonicalize(path: str) -> Result[string.String, Error]:
|
|
503
|
+
var storage = arena.create(path.len + 1)
|
|
504
|
+
defer storage.release()
|
|
505
|
+
|
|
506
|
+
var raw_text = zero[c.mt_fs_string]
|
|
507
|
+
var raw_error = zero[c.mt_fs_error]
|
|
508
|
+
let status_code = c.mt_fs_canonicalize(storage.to_cstr(path), raw_text, raw_error)
|
|
509
|
+
if status_code != 0:
|
|
510
|
+
return Result[string.String, Error].failure(error= take_error(raw_error, "fs canonicalize failed"))
|
|
511
|
+
|
|
512
|
+
return validate_utf8_string(take_owned_string(raw_text.data, raw_text.len), "fs.canonicalize requires UTF-8 text")
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
public function create_temporary_directory(parent_dir: str, prefix: str) -> Result[string.String, Error]:
|
|
516
|
+
if prefix.len == 0:
|
|
517
|
+
return Result[
|
|
518
|
+
string.String,
|
|
519
|
+
Error
|
|
520
|
+
].failure(error= static_error("fs.create_temporary_directory requires a non-empty prefix"))
|
|
521
|
+
|
|
522
|
+
var storage = arena.create(parent_dir.len + prefix.len + 2)
|
|
523
|
+
defer storage.release()
|
|
524
|
+
|
|
525
|
+
var raw_text = zero[c.mt_fs_string]
|
|
526
|
+
var raw_error = zero[c.mt_fs_error]
|
|
527
|
+
let status_code = c.mt_fs_create_temporary_directory(
|
|
528
|
+
storage.to_cstr(parent_dir),
|
|
529
|
+
storage.to_cstr(prefix),
|
|
530
|
+
raw_text,
|
|
531
|
+
raw_error
|
|
532
|
+
)
|
|
533
|
+
if status_code != 0:
|
|
534
|
+
return Result[string.String, Error].failure(error= take_error(
|
|
535
|
+
raw_error,
|
|
536
|
+
"fs create temporary directory failed"
|
|
537
|
+
))
|
|
538
|
+
|
|
539
|
+
return validate_utf8_string(
|
|
540
|
+
take_owned_string(raw_text.data, raw_text.len),
|
|
541
|
+
"fs.create_temporary_directory requires UTF-8 text"
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
public function create_temporary_directory_in_system_temp(prefix: str) -> Result[string.String, Error]:
|
|
546
|
+
var root = temporary_directory()
|
|
547
|
+
defer root.release()
|
|
548
|
+
return create_temporary_directory(root.as_str(), prefix)
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
public function create_temporary_file(parent_dir: str, prefix: str, suffix: str) -> Result[string.String, Error]:
|
|
552
|
+
if prefix.len == 0:
|
|
553
|
+
return Result[
|
|
554
|
+
string.String,
|
|
555
|
+
Error
|
|
556
|
+
].failure(error= static_error("fs.create_temporary_file requires a non-empty prefix"))
|
|
557
|
+
|
|
558
|
+
var storage = arena.create(parent_dir.len + prefix.len + suffix.len + 3)
|
|
559
|
+
defer storage.release()
|
|
560
|
+
|
|
561
|
+
var raw_text = zero[c.mt_fs_string]
|
|
562
|
+
var raw_error = zero[c.mt_fs_error]
|
|
563
|
+
let status_code = c.mt_fs_create_temporary_file(
|
|
564
|
+
storage.to_cstr(parent_dir),
|
|
565
|
+
storage.to_cstr(prefix),
|
|
566
|
+
storage.to_cstr(suffix),
|
|
567
|
+
raw_text,
|
|
568
|
+
raw_error
|
|
569
|
+
)
|
|
570
|
+
if status_code != 0:
|
|
571
|
+
return Result[string.String, Error].failure(error= take_error(raw_error, "fs create temporary file failed"))
|
|
572
|
+
|
|
573
|
+
return validate_utf8_string(
|
|
574
|
+
take_owned_string(raw_text.data, raw_text.len),
|
|
575
|
+
"fs.create_temporary_file requires UTF-8 text"
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
public function create_temporary_file_in_system_temp(prefix: str, suffix: str) -> Result[string.String, Error]:
|
|
580
|
+
var root = temporary_directory()
|
|
581
|
+
defer root.release()
|
|
582
|
+
return create_temporary_file(root.as_str(), prefix, suffix)
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
public function list_entries(path: str) -> Result[Entries, Error]:
|
|
586
|
+
var storage = arena.create(path.len + 1)
|
|
587
|
+
defer storage.release()
|
|
588
|
+
|
|
589
|
+
var raw_entries = zero[c.mt_fs_entries]
|
|
590
|
+
var raw_error = zero[c.mt_fs_error]
|
|
591
|
+
let status_code = c.mt_fs_list_entries(storage.to_cstr(path), raw_entries, raw_error)
|
|
592
|
+
if status_code != 0:
|
|
593
|
+
return Result[Entries, Error].failure(error= take_error(raw_error, "fs list entries failed"))
|
|
594
|
+
|
|
595
|
+
var values = vec.Vec[string.String].with_capacity(raw_entries.count)
|
|
596
|
+
var index: ptr_uint = 0
|
|
597
|
+
if raw_entries.count != 0 and (raw_entries.data == null or raw_entries.lengths == null):
|
|
598
|
+
fatal(c"fs.list_entries missing storage")
|
|
599
|
+
|
|
600
|
+
unsafe:
|
|
601
|
+
let data_ptr = ptr[ptr[char]]<-raw_entries.data
|
|
602
|
+
let length_ptr = ptr[ptr_uint]<-raw_entries.lengths
|
|
603
|
+
while index < raw_entries.count:
|
|
604
|
+
let owned = take_owned_string(read(data_ptr + index), read(length_ptr + index))
|
|
605
|
+
match validate_utf8_string(owned, "fs.list_entries requires UTF-8 entry names"):
|
|
606
|
+
Result.failure as payload:
|
|
607
|
+
var remaining = index + 1
|
|
608
|
+
while remaining < raw_entries.count:
|
|
609
|
+
heap.release(read(data_ptr + remaining))
|
|
610
|
+
remaining += 1
|
|
611
|
+
heap.release(raw_entries.data)
|
|
612
|
+
heap.release(raw_entries.lengths)
|
|
613
|
+
release_string_values(ref_of(values))
|
|
614
|
+
return Result[Entries, Error].failure(error= payload.error)
|
|
615
|
+
Result.success as validated_payload:
|
|
616
|
+
values.push(validated_payload.value)
|
|
617
|
+
|
|
618
|
+
index += 1
|
|
619
|
+
|
|
620
|
+
heap.release(raw_entries.data)
|
|
621
|
+
heap.release(raw_entries.lengths)
|
|
622
|
+
return Result[Entries, Error].success(value= Entries(values = values))
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
public function list_files_recursive(path: str) -> Result[Entries, Error]:
|
|
626
|
+
if is_file(path):
|
|
627
|
+
var values = vec.Vec[string.String].create()
|
|
628
|
+
values.push(string.String.from_str(path))
|
|
629
|
+
return Result[Entries, Error].success(value= Entries(values = values))
|
|
630
|
+
|
|
631
|
+
if not is_directory(path):
|
|
632
|
+
if exists(path):
|
|
633
|
+
return Result[
|
|
634
|
+
Entries,
|
|
635
|
+
Error
|
|
636
|
+
].failure(error= static_error("fs.list_files_recursive supports only regular files and directories"))
|
|
637
|
+
|
|
638
|
+
return Result[Entries, Error].failure(error= static_error("fs.list_files_recursive path does not exist"))
|
|
639
|
+
|
|
640
|
+
var values = vec.Vec[string.String].create()
|
|
641
|
+
match collect_files_recursive(path, ref_of(values)):
|
|
642
|
+
Result.failure as payload:
|
|
643
|
+
release_string_values(ref_of(values))
|
|
644
|
+
return Result[Entries, Error].failure(error= payload.error)
|
|
645
|
+
Result.success:
|
|
646
|
+
return Result[Entries, Error].success(value= Entries(values = values))
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
function collect_files_recursive(path: str, output: ref[vec.Vec[string.String]]) -> Result[bool, Error]:
|
|
650
|
+
if is_file(path):
|
|
651
|
+
insert_sorted_string(output, string.String.from_str(path))
|
|
652
|
+
return Result[bool, Error].success(value= true)
|
|
653
|
+
|
|
654
|
+
if not is_directory(path):
|
|
655
|
+
return Result[bool, Error].success(value= true)
|
|
656
|
+
|
|
657
|
+
match list_entries(path):
|
|
658
|
+
Result.failure as payload:
|
|
659
|
+
return Result[bool, Error].failure(error= payload.error)
|
|
660
|
+
Result.success as payload:
|
|
661
|
+
var entries = payload.value
|
|
662
|
+
defer entries.release()
|
|
663
|
+
|
|
664
|
+
var index: ptr_uint = 0
|
|
665
|
+
while index < entries.len():
|
|
666
|
+
match entries.get(index):
|
|
667
|
+
Option.none:
|
|
668
|
+
return Result[bool, Error].failure(error= static_error("fs.list_files_recursive missing entry"))
|
|
669
|
+
Option.some as entry_payload:
|
|
670
|
+
var child_path = path_ops.join(path, entry_payload.value)
|
|
671
|
+
match collect_files_recursive(child_path.as_str(), output):
|
|
672
|
+
Result.failure as child_payload:
|
|
673
|
+
child_path.release()
|
|
674
|
+
return Result[bool, Error].failure(error= child_payload.error)
|
|
675
|
+
Result.success:
|
|
676
|
+
pass
|
|
677
|
+
child_path.release()
|
|
678
|
+
index += 1
|
|
679
|
+
|
|
680
|
+
return Result[bool, Error].success(value= true)
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
function insert_sorted_string(values: ref[vec.Vec[string.String]], value: string.String) -> void:
|
|
684
|
+
var index: ptr_uint = 0
|
|
685
|
+
while index < values.len():
|
|
686
|
+
let existing_ptr = values.get(index) else:
|
|
687
|
+
fatal(c"fs.insert_sorted_string missing value")
|
|
688
|
+
|
|
689
|
+
unsafe:
|
|
690
|
+
if string_less(value.as_str(), read(existing_ptr).as_str()):
|
|
691
|
+
break
|
|
692
|
+
|
|
693
|
+
index += 1
|
|
694
|
+
|
|
695
|
+
if not values.insert(index, value):
|
|
696
|
+
fatal(c"fs.insert_sorted_string insert failed")
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
function string_less(left: str, right: str) -> bool:
|
|
700
|
+
var index: ptr_uint = 0
|
|
701
|
+
let shared_len = if left.len < right.len: left.len else: right.len
|
|
702
|
+
while index < shared_len:
|
|
703
|
+
let left_value = left.byte_at(index)
|
|
704
|
+
let right_value = right.byte_at(index)
|
|
705
|
+
if left_value < right_value:
|
|
706
|
+
return true
|
|
707
|
+
if left_value > right_value:
|
|
708
|
+
return false
|
|
709
|
+
index += 1
|
|
710
|
+
|
|
711
|
+
return left.len < right.len
|