@shd101wyy/yo 0.1.26 → 0.1.27
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.
- package/.github/skills/yo-async-effects/SKILL.md +4 -4
- package/.github/skills/yo-async-effects/async-effects-recipes.md +34 -34
- package/.github/skills/yo-core-patterns/SKILL.md +1 -1
- package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +26 -26
- package/.github/skills/yo-project-workflow/SKILL.md +6 -3
- package/.github/skills/yo-project-workflow/workflow-cheatsheet.md +34 -11
- package/.github/skills/yo-syntax/SKILL.md +7 -6
- package/.github/skills/yo-syntax/syntax-cheatsheet.md +73 -60
- package/.github/skills/yo-wasm-integration/wasm-integration-cheatsheet.md +3 -3
- package/README.md +10 -8
- package/out/cjs/index.cjs +456 -438
- package/out/cjs/yo-cli.cjs +576 -543
- package/out/cjs/yo-lsp.cjs +559 -532
- package/out/esm/index.mjs +281 -263
- package/out/types/src/formatter.d.ts +11 -0
- package/out/types/src/lsp/formatting.d.ts +2 -0
- package/out/types/src/tests/formatter.test.d.ts +1 -0
- package/out/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/std/alg/hash.yo +13 -21
- package/std/allocator.yo +25 -40
- package/std/async.yo +3 -7
- package/std/build.yo +105 -151
- package/std/cli/arg_parser.yo +184 -169
- package/std/collections/array_list.yo +350 -314
- package/std/collections/btree_map.yo +142 -131
- package/std/collections/deque.yo +132 -128
- package/std/collections/hash_map.yo +542 -566
- package/std/collections/hash_set.yo +623 -687
- package/std/collections/linked_list.yo +275 -293
- package/std/collections/ordered_map.yo +113 -85
- package/std/collections/priority_queue.yo +73 -73
- package/std/crypto/md5.yo +191 -95
- package/std/crypto/random.yo +56 -64
- package/std/crypto/sha256.yo +151 -107
- package/std/encoding/base64.yo +87 -81
- package/std/encoding/hex.yo +43 -50
- package/std/encoding/html.yo +56 -81
- package/std/encoding/html_char_utils.yo +7 -13
- package/std/encoding/html_entities.yo +2248 -2253
- package/std/encoding/json.yo +316 -224
- package/std/encoding/punycode.yo +86 -116
- package/std/encoding/toml.yo +67 -66
- package/std/encoding/utf16.yo +37 -44
- package/std/env.yo +62 -91
- package/std/error.yo +7 -15
- package/std/fmt/display.yo +5 -9
- package/std/fmt/index.yo +8 -14
- package/std/fmt/to_string.yo +330 -315
- package/std/fmt/writer.yo +58 -87
- package/std/fs/dir.yo +83 -102
- package/std/fs/file.yo +147 -180
- package/std/fs/metadata.yo +45 -78
- package/std/fs/temp.yo +55 -65
- package/std/fs/types.yo +27 -40
- package/std/fs/walker.yo +53 -68
- package/std/gc.yo +5 -8
- package/std/glob.yo +30 -43
- package/std/http/client.yo +107 -120
- package/std/http/http.yo +106 -96
- package/std/http/index.yo +4 -6
- package/std/imm/list.yo +88 -93
- package/std/imm/map.yo +528 -464
- package/std/imm/set.yo +52 -57
- package/std/imm/sorted_map.yo +340 -286
- package/std/imm/sorted_set.yo +57 -63
- package/std/imm/string.yo +404 -345
- package/std/imm/vec.yo +173 -181
- package/std/io/reader.yo +3 -6
- package/std/io/writer.yo +4 -8
- package/std/libc/assert.yo +5 -9
- package/std/libc/ctype.yo +32 -22
- package/std/libc/dirent.yo +26 -25
- package/std/libc/errno.yo +164 -90
- package/std/libc/fcntl.yo +52 -45
- package/std/libc/float.yo +66 -44
- package/std/libc/limits.yo +42 -33
- package/std/libc/math.yo +53 -82
- package/std/libc/signal.yo +72 -47
- package/std/libc/stdatomic.yo +217 -188
- package/std/libc/stdint.yo +5 -29
- package/std/libc/stdio.yo +5 -29
- package/std/libc/stdlib.yo +32 -39
- package/std/libc/string.yo +5 -23
- package/std/libc/sys/stat.yo +58 -56
- package/std/libc/time.yo +5 -19
- package/std/libc/unistd.yo +5 -20
- package/std/libc/wctype.yo +6 -9
- package/std/libc/windows.yo +26 -30
- package/std/log.yo +41 -55
- package/std/net/addr.yo +102 -97
- package/std/net/dns.yo +27 -28
- package/std/net/errors.yo +50 -49
- package/std/net/tcp.yo +113 -124
- package/std/net/udp.yo +55 -66
- package/std/os/env.yo +35 -33
- package/std/os/signal.yo +15 -25
- package/std/path.yo +276 -311
- package/std/prelude.yo +6304 -4315
- package/std/process/command.yo +87 -103
- package/std/process/index.yo +12 -31
- package/std/regex/compiler.yo +196 -95
- package/std/regex/flags.yo +58 -39
- package/std/regex/index.yo +157 -173
- package/std/regex/match.yo +20 -31
- package/std/regex/node.yo +134 -152
- package/std/regex/parser.yo +283 -259
- package/std/regex/unicode.yo +172 -202
- package/std/regex/vm.yo +155 -171
- package/std/string/index.yo +5 -7
- package/std/string/rune.yo +45 -55
- package/std/string/string.yo +937 -964
- package/std/string/string_builder.yo +94 -104
- package/std/string/unicode.yo +46 -64
- package/std/sync/channel.yo +72 -73
- package/std/sync/cond.yo +31 -36
- package/std/sync/mutex.yo +30 -32
- package/std/sync/once.yo +13 -16
- package/std/sync/rwlock.yo +26 -31
- package/std/sync/waitgroup.yo +20 -25
- package/std/sys/advise.yo +16 -24
- package/std/sys/bufio/buf_reader.yo +77 -93
- package/std/sys/bufio/buf_writer.yo +52 -65
- package/std/sys/clock.yo +4 -9
- package/std/sys/constants.yo +77 -61
- package/std/sys/copy.yo +4 -10
- package/std/sys/dir.yo +26 -43
- package/std/sys/dns.yo +41 -61
- package/std/sys/errors.yo +95 -103
- package/std/sys/events.yo +45 -57
- package/std/sys/externs.yo +319 -267
- package/std/sys/fallocate.yo +7 -11
- package/std/sys/fcntl.yo +14 -22
- package/std/sys/file.yo +26 -40
- package/std/sys/future.yo +5 -8
- package/std/sys/iov.yo +12 -25
- package/std/sys/lock.yo +12 -13
- package/std/sys/mmap.yo +38 -43
- package/std/sys/path.yo +3 -8
- package/std/sys/perm.yo +7 -21
- package/std/sys/pipe.yo +5 -12
- package/std/sys/process.yo +23 -29
- package/std/sys/seek.yo +10 -12
- package/std/sys/signal.yo +7 -13
- package/std/sys/signals.yo +52 -35
- package/std/sys/socket.yo +63 -58
- package/std/sys/socketpair.yo +3 -6
- package/std/sys/sockinfo.yo +11 -20
- package/std/sys/statfs.yo +11 -34
- package/std/sys/statx.yo +25 -52
- package/std/sys/sysinfo.yo +15 -20
- package/std/sys/tcp.yo +62 -92
- package/std/sys/temp.yo +5 -9
- package/std/sys/time.yo +5 -15
- package/std/sys/timer.yo +6 -11
- package/std/sys/tty.yo +10 -18
- package/std/sys/udp.yo +22 -39
- package/std/sys/umask.yo +3 -6
- package/std/sys/unix.yo +33 -52
- package/std/testing/bench.yo +49 -52
- package/std/thread.yo +10 -15
- package/std/time/datetime.yo +105 -89
- package/std/time/duration.yo +43 -56
- package/std/time/instant.yo +13 -18
- package/std/time/sleep.yo +5 -9
- package/std/url/index.yo +184 -209
- package/std/worker.yo +6 -10
package/std/path.yo
CHANGED
|
@@ -1,55 +1,50 @@
|
|
|
1
1
|
//! Cross-platform filesystem path manipulation.
|
|
2
|
-
|
|
3
|
-
open
|
|
4
|
-
open
|
|
5
|
-
open import "./collections/array_list";
|
|
6
|
-
|
|
2
|
+
open(import("./string"));
|
|
3
|
+
open(import("./fmt"));
|
|
4
|
+
open(import("./collections/array_list"));
|
|
7
5
|
/// Path parsing error variants.
|
|
8
6
|
PathError :: enum(
|
|
9
7
|
/// The path string was empty.
|
|
10
8
|
EmptyPath,
|
|
11
9
|
/// The path string was malformed.
|
|
12
|
-
InvalidPath(message: String)
|
|
10
|
+
InvalidPath(message : String)
|
|
13
11
|
);
|
|
14
|
-
export
|
|
15
|
-
|
|
12
|
+
export(PathError);
|
|
16
13
|
/// Platform-specific path separator: `'/'` on Unix, `'\\'` on Windows.
|
|
17
14
|
PATH_SEPARATOR :: cond(
|
|
18
|
-
(__yo_process_platform() == "windows") => u8(92),
|
|
19
|
-
|
|
15
|
+
(__yo_process_platform() == "windows") => u8(92),
|
|
16
|
+
// '\' for Windows
|
|
17
|
+
true => u8(47) // '/' for Unix
|
|
20
18
|
);
|
|
21
|
-
export
|
|
22
|
-
|
|
19
|
+
export(PATH_SEPARATOR);
|
|
23
20
|
/// Platform-specific path list delimiter: `':'` on Unix, `';'` on Windows.
|
|
24
21
|
PATH_DELIMITER :: cond(
|
|
25
|
-
(__yo_process_platform() == "windows") => u8(59),
|
|
26
|
-
|
|
22
|
+
(__yo_process_platform() == "windows") => u8(59),
|
|
23
|
+
// ';' for Windows
|
|
24
|
+
true => u8(58) // ':' for Unix
|
|
27
25
|
);
|
|
28
|
-
export
|
|
29
|
-
|
|
26
|
+
export(PATH_DELIMITER);
|
|
30
27
|
/// Filesystem path with segment-based operations.
|
|
31
28
|
/// Paths are normalized on construction: `.` segments are removed, `..` pops the parent.
|
|
32
29
|
Path :: object(
|
|
33
|
-
_segments: ArrayList(String),
|
|
34
|
-
_is_absolute: bool
|
|
30
|
+
_segments : ArrayList(String),
|
|
31
|
+
_is_absolute : bool
|
|
35
32
|
);
|
|
36
|
-
impl(
|
|
33
|
+
impl(
|
|
34
|
+
Path,
|
|
37
35
|
/// Create a new path from a string. Normalizes separators and resolves `.`/`..`.
|
|
38
|
-
new : (fn(path_str: String) -> Self)({
|
|
36
|
+
new : (fn(path_str : String) -> Self)({
|
|
39
37
|
segments := ArrayList(String).new();
|
|
40
38
|
is_abs := false;
|
|
41
|
-
|
|
42
39
|
// Handle empty path early
|
|
43
40
|
cond(
|
|
44
41
|
(path_str.is_empty()) => {
|
|
45
|
-
return
|
|
42
|
+
return(Self(_segments : segments, _is_absolute : is_abs));
|
|
46
43
|
},
|
|
47
44
|
true => ()
|
|
48
45
|
);
|
|
49
|
-
|
|
50
46
|
// Normalize backslashes to forward slashes for cross-platform support
|
|
51
47
|
normalized := path_str.replace_all(String.from("\\"), String.from("/"));
|
|
52
|
-
|
|
53
48
|
// Check if path is absolute
|
|
54
49
|
// Unix: starts with '/'
|
|
55
50
|
// Windows: starts with drive letter like 'C:' or UNC path '\\'
|
|
@@ -57,7 +52,8 @@ impl(Path,
|
|
|
57
52
|
cond(
|
|
58
53
|
(bytes.len() > usize(0)) => {
|
|
59
54
|
first_byte := bytes.get(usize(0));
|
|
60
|
-
match(
|
|
55
|
+
match(
|
|
56
|
+
first_byte,
|
|
61
57
|
.Some(b) => cond(
|
|
62
58
|
// Unix-style absolute path (starts with /)
|
|
63
59
|
(b == u8(47)) => {
|
|
@@ -68,7 +64,8 @@ impl(Path,
|
|
|
68
64
|
cond(
|
|
69
65
|
(bytes.len() >= usize(2)) => {
|
|
70
66
|
second_byte_opt := bytes.get(usize(1));
|
|
71
|
-
match(
|
|
67
|
+
match(
|
|
68
|
+
second_byte_opt,
|
|
72
69
|
.Some(second_byte) => cond(
|
|
73
70
|
// Check if second byte is ':' (colon)
|
|
74
71
|
(second_byte == u8(58)) => {
|
|
@@ -91,51 +88,54 @@ impl(Path,
|
|
|
91
88
|
}
|
|
92
89
|
),
|
|
93
90
|
.None => ()
|
|
94
|
-
);
|
|
91
|
+
);
|
|
95
92
|
},
|
|
96
93
|
true => ()
|
|
97
94
|
);
|
|
98
|
-
|
|
99
95
|
// Split path by "/" separator (backslashes already normalized)
|
|
100
96
|
sep := String.from("/");
|
|
101
97
|
parts := normalized.split(sep);
|
|
102
|
-
|
|
103
98
|
// Process each part
|
|
104
99
|
i := usize(0);
|
|
105
|
-
while
|
|
106
|
-
(i = (i + usize(1))),
|
|
107
|
-
{
|
|
100
|
+
while(i < parts.len(), i = (i + usize(1)), {
|
|
108
101
|
part_opt := parts.get(i);
|
|
109
|
-
match(
|
|
102
|
+
match(
|
|
103
|
+
part_opt,
|
|
110
104
|
.Some(part) => {
|
|
111
105
|
// Skip empty segments (from leading/trailing slashes)
|
|
112
106
|
cond(
|
|
113
107
|
(part.is_empty()) => (),
|
|
114
108
|
true => {
|
|
115
109
|
// Check if it's "." (current directory)
|
|
116
|
-
is_dot := (
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
110
|
+
is_dot := (
|
|
111
|
+
(part.len() == usize(1)) && {
|
|
112
|
+
byte_opt := part.as_bytes().get(usize(0));
|
|
113
|
+
match(
|
|
114
|
+
byte_opt,
|
|
115
|
+
.Some(b) => (b == u8(46)),
|
|
116
|
+
.None => false
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
);
|
|
124
120
|
// Check if it's ".." (parent directory)
|
|
125
|
-
is_dotdot := (
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
121
|
+
is_dotdot := (
|
|
122
|
+
(part.len() == usize(2)) && {
|
|
123
|
+
b0_opt := part.as_bytes().get(usize(0));
|
|
124
|
+
b1_opt := part.as_bytes().get(usize(1));
|
|
125
|
+
match(
|
|
126
|
+
b0_opt,
|
|
127
|
+
.Some(b0) => match(
|
|
128
|
+
b1_opt,
|
|
129
|
+
.Some(b1) => ((b0 == u8(46)) && (b1 == u8(46))),
|
|
130
|
+
.None => false
|
|
131
|
+
),
|
|
131
132
|
.None => false
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
});
|
|
136
|
-
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
);
|
|
137
136
|
cond(
|
|
138
|
-
(is_dot) => (),
|
|
137
|
+
(is_dot) => (),
|
|
138
|
+
// Skip "."
|
|
139
139
|
(is_dotdot) => {
|
|
140
140
|
// Pop the last segment if exists
|
|
141
141
|
cond(
|
|
@@ -154,355 +154,322 @@ impl(Path,
|
|
|
154
154
|
},
|
|
155
155
|
.None => ()
|
|
156
156
|
);
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
return Self(_segments: segments, _is_absolute: is_abs);
|
|
157
|
+
});
|
|
158
|
+
return(Self(_segments : segments, _is_absolute : is_abs));
|
|
160
159
|
}),
|
|
161
|
-
|
|
162
160
|
/// Create a path from a C string pointer.
|
|
163
|
-
from_cstr : (fn(cstr: *(u8)) -> Self)({
|
|
161
|
+
from_cstr : (fn(cstr : *(u8)) -> Self)({
|
|
164
162
|
str_result := String.from_cstr(cstr);
|
|
165
|
-
match(
|
|
163
|
+
match(
|
|
164
|
+
str_result,
|
|
166
165
|
.Ok(s) => {
|
|
167
|
-
return
|
|
166
|
+
return(Self.new(s));
|
|
168
167
|
},
|
|
169
168
|
.Err(_) => {
|
|
170
169
|
// Return empty path on error
|
|
171
|
-
return
|
|
170
|
+
return(Self(_segments : ArrayList(String).new(), _is_absolute : false));
|
|
172
171
|
}
|
|
173
172
|
);
|
|
174
173
|
}),
|
|
175
|
-
|
|
176
174
|
/// Join this path with another. If `other` is absolute, returns `other`.
|
|
177
|
-
join : (fn(self: Self, other: Self) -> Self)({
|
|
175
|
+
join : (fn(self : Self, other : Self) -> Self)({
|
|
178
176
|
// If other is absolute, return other
|
|
179
177
|
cond(
|
|
180
178
|
(other._is_absolute) => {
|
|
181
|
-
return
|
|
179
|
+
return(other);
|
|
182
180
|
},
|
|
183
181
|
true => ()
|
|
184
182
|
);
|
|
185
|
-
|
|
186
183
|
// Otherwise, concatenate segments
|
|
187
|
-
new_segments := ArrayList(String).with_capacity(
|
|
188
|
-
|
|
184
|
+
new_segments := ArrayList(String).with_capacity(self._segments.len() + other._segments.len());
|
|
189
185
|
// Copy self segments
|
|
190
186
|
i := usize(0);
|
|
191
|
-
while
|
|
192
|
-
(i = (i + usize(1))),
|
|
193
|
-
{
|
|
187
|
+
while(i < self._segments.len(), i = (i + usize(1)), {
|
|
194
188
|
seg := self._segments.get(i);
|
|
195
|
-
match(
|
|
189
|
+
match(
|
|
190
|
+
seg,
|
|
196
191
|
.Some(s) => {
|
|
197
192
|
new_segments.push(s);
|
|
198
193
|
},
|
|
199
194
|
.None => ()
|
|
200
195
|
);
|
|
201
|
-
};
|
|
202
|
-
|
|
196
|
+
});
|
|
203
197
|
// Copy other segments
|
|
204
198
|
j := usize(0);
|
|
205
|
-
while
|
|
206
|
-
(j = (j + usize(1))),
|
|
207
|
-
{
|
|
199
|
+
while(j < other._segments.len(), j = (j + usize(1)), {
|
|
208
200
|
seg := other._segments.get(j);
|
|
209
|
-
match(
|
|
201
|
+
match(
|
|
202
|
+
seg,
|
|
210
203
|
.Some(s) => {
|
|
211
204
|
new_segments.push(s);
|
|
212
205
|
},
|
|
213
206
|
.None => ()
|
|
214
207
|
);
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
return Self(_segments: new_segments, _is_absolute: self._is_absolute);
|
|
208
|
+
});
|
|
209
|
+
return(Self(_segments : new_segments, _is_absolute : self._is_absolute));
|
|
218
210
|
}),
|
|
219
|
-
|
|
220
211
|
/// Get the parent directory path, or `.None` if there are no segments.
|
|
221
|
-
parent : (fn(self: Self) -> Option(Self))({
|
|
212
|
+
parent : (fn(self : Self) -> Option(Self))({
|
|
222
213
|
cond(
|
|
223
214
|
(self._segments.len() == usize(0)) => {
|
|
224
|
-
return
|
|
215
|
+
return(.None);
|
|
225
216
|
},
|
|
226
217
|
true => ()
|
|
227
218
|
);
|
|
228
|
-
|
|
229
|
-
new_segments := ArrayList(String).with_capacity((self._segments.len() - usize(1)));
|
|
219
|
+
new_segments := ArrayList(String).with_capacity(self._segments.len() - usize(1));
|
|
230
220
|
i := usize(0);
|
|
231
|
-
while
|
|
232
|
-
(i = (i + usize(1))),
|
|
233
|
-
{
|
|
221
|
+
while(i < (self._segments.len() - usize(1)), i = (i + usize(1)), {
|
|
234
222
|
seg := self._segments.get(i);
|
|
235
|
-
match(
|
|
223
|
+
match(
|
|
224
|
+
seg,
|
|
236
225
|
.Some(s) => {
|
|
237
226
|
new_segments.push(s);
|
|
238
227
|
},
|
|
239
228
|
.None => ()
|
|
240
229
|
);
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
return .Some(Self(_segments: new_segments, _is_absolute: self._is_absolute));
|
|
230
|
+
});
|
|
231
|
+
return(.Some(Self(_segments : new_segments, _is_absolute : self._is_absolute)));
|
|
244
232
|
}),
|
|
245
|
-
|
|
246
233
|
/// Get the last path segment (filename), or `.None` for empty paths.
|
|
247
|
-
file_name : (fn(self: Self) -> Option(String))({
|
|
234
|
+
file_name : (fn(self : Self) -> Option(String))({
|
|
248
235
|
cond(
|
|
249
236
|
(self._segments.len() == usize(0)) => {
|
|
250
|
-
return
|
|
237
|
+
return(.None);
|
|
251
238
|
},
|
|
252
239
|
true => ()
|
|
253
240
|
);
|
|
254
|
-
|
|
255
|
-
return self._segments.get((self._segments.len() - usize(1)));
|
|
241
|
+
return(self._segments.get(self._segments.len() - usize(1)));
|
|
256
242
|
}),
|
|
257
|
-
|
|
258
243
|
/// Get the filename without its extension, or `.None` for empty paths.
|
|
259
|
-
file_stem : (fn(self: Self) -> Option(String))({
|
|
244
|
+
file_stem : (fn(self : Self) -> Option(String))({
|
|
260
245
|
name_opt := self.file_name();
|
|
261
|
-
match(
|
|
246
|
+
match(
|
|
247
|
+
name_opt,
|
|
262
248
|
.Some(name) => {
|
|
263
249
|
// Find the last dot using String methods
|
|
264
250
|
dot := String.from(".");
|
|
265
251
|
last_dot_result := name.last_index_of(dot);
|
|
266
|
-
match(
|
|
252
|
+
match(
|
|
253
|
+
last_dot_result,
|
|
267
254
|
.Some(idx) => {
|
|
268
255
|
// Return substring before the dot
|
|
269
256
|
stem := name.substring(usize(0), idx);
|
|
270
|
-
return
|
|
257
|
+
return(.Some(stem));
|
|
271
258
|
},
|
|
272
259
|
.None => {
|
|
273
260
|
// No dot found, return the whole name
|
|
274
|
-
return
|
|
261
|
+
return(.Some(name));
|
|
275
262
|
}
|
|
276
263
|
);
|
|
277
264
|
},
|
|
278
265
|
.None => {
|
|
279
|
-
return
|
|
266
|
+
return(.None);
|
|
280
267
|
}
|
|
281
268
|
);
|
|
282
269
|
}),
|
|
283
|
-
|
|
284
270
|
/// Get the file extension (without the dot), or `.None` if there is none.
|
|
285
|
-
extension : (fn(self: Self) -> Option(String))({
|
|
271
|
+
extension : (fn(self : Self) -> Option(String))({
|
|
286
272
|
name_opt := self.file_name();
|
|
287
|
-
match(
|
|
273
|
+
match(
|
|
274
|
+
name_opt,
|
|
288
275
|
.Some(name) => {
|
|
289
276
|
// Find the last dot using String methods
|
|
290
277
|
dot := String.from(".");
|
|
291
278
|
last_dot_result := name.last_index_of(dot);
|
|
292
|
-
match(
|
|
279
|
+
match(
|
|
280
|
+
last_dot_result,
|
|
293
281
|
.Some(idx) => {
|
|
294
282
|
// Return substring after the dot
|
|
295
|
-
ext := name.substring(
|
|
283
|
+
ext := name.substring(idx + usize(1), name.len());
|
|
296
284
|
cond(
|
|
297
285
|
(ext.is_empty()) => {
|
|
298
|
-
return
|
|
286
|
+
return(.None);
|
|
299
287
|
},
|
|
300
288
|
true => {
|
|
301
|
-
return
|
|
289
|
+
return(.Some(ext));
|
|
302
290
|
}
|
|
303
291
|
);
|
|
304
292
|
},
|
|
305
293
|
.None => {
|
|
306
294
|
// No dot found, no extension
|
|
307
|
-
return
|
|
295
|
+
return(.None);
|
|
308
296
|
}
|
|
309
297
|
);
|
|
310
298
|
},
|
|
311
299
|
.None => {
|
|
312
|
-
return
|
|
300
|
+
return(.None);
|
|
313
301
|
}
|
|
314
302
|
);
|
|
315
303
|
}),
|
|
316
|
-
|
|
317
304
|
/// Return a new path with the extension replaced by `ext`.
|
|
318
|
-
with_extension : (fn(self: Self, ext: String) -> Self)({
|
|
305
|
+
with_extension : (fn(self : Self, ext : String) -> Self)({
|
|
319
306
|
cond(
|
|
320
307
|
(self._segments.len() == usize(0)) => {
|
|
321
|
-
return
|
|
308
|
+
return(self);
|
|
322
309
|
},
|
|
323
310
|
true => ()
|
|
324
311
|
);
|
|
325
|
-
|
|
326
312
|
// Get the file stem
|
|
327
313
|
stem_opt := self.file_stem();
|
|
328
|
-
match(
|
|
314
|
+
match(
|
|
315
|
+
stem_opt,
|
|
329
316
|
.Some(stem) => {
|
|
330
317
|
// Build new file name with extension
|
|
331
318
|
dot := String.from(".");
|
|
332
319
|
new_name := stem.concat(dot).concat(ext);
|
|
333
|
-
|
|
334
320
|
// Replace the last segment
|
|
335
321
|
new_segments := ArrayList(String).with_capacity(self._segments.len());
|
|
336
322
|
i := usize(0);
|
|
337
|
-
while
|
|
338
|
-
(i = (i + usize(1))),
|
|
339
|
-
{
|
|
323
|
+
while(i < (self._segments.len() - usize(1)), i = (i + usize(1)), {
|
|
340
324
|
seg := self._segments.get(i);
|
|
341
|
-
match(
|
|
325
|
+
match(
|
|
326
|
+
seg,
|
|
342
327
|
.Some(s) => {
|
|
343
328
|
new_segments.push(s);
|
|
344
329
|
},
|
|
345
330
|
.None => ()
|
|
346
331
|
);
|
|
347
|
-
};
|
|
332
|
+
});
|
|
348
333
|
new_segments.push(new_name);
|
|
349
|
-
|
|
350
|
-
return Self(_segments: new_segments, _is_absolute: self._is_absolute);
|
|
334
|
+
return(Self(_segments : new_segments, _is_absolute : self._is_absolute));
|
|
351
335
|
},
|
|
352
336
|
.None => {
|
|
353
|
-
return
|
|
337
|
+
return(self);
|
|
354
338
|
}
|
|
355
339
|
);
|
|
356
340
|
}),
|
|
357
|
-
|
|
358
341
|
/// Return a new path with the last segment replaced by `name`.
|
|
359
|
-
with_file_name : (fn(self: Self, name: String) -> Self)({
|
|
342
|
+
with_file_name : (fn(self : Self, name : String) -> Self)({
|
|
360
343
|
cond(
|
|
361
344
|
(self._segments.len() == usize(0)) => {
|
|
362
345
|
// Empty path, just return a new path with the name
|
|
363
346
|
segments := ArrayList(String).new();
|
|
364
347
|
segments.push(name);
|
|
365
|
-
return
|
|
348
|
+
return(Self(_segments : segments, _is_absolute : self._is_absolute));
|
|
366
349
|
},
|
|
367
350
|
true => ()
|
|
368
351
|
);
|
|
369
|
-
|
|
370
352
|
// Replace the last segment
|
|
371
353
|
new_segments := ArrayList(String).with_capacity(self._segments.len());
|
|
372
354
|
i := usize(0);
|
|
373
|
-
while
|
|
374
|
-
(i = (i + usize(1))),
|
|
375
|
-
{
|
|
355
|
+
while(i < (self._segments.len() - usize(1)), i = (i + usize(1)), {
|
|
376
356
|
seg := self._segments.get(i);
|
|
377
|
-
match(
|
|
357
|
+
match(
|
|
358
|
+
seg,
|
|
378
359
|
.Some(s) => {
|
|
379
360
|
new_segments.push(s);
|
|
380
361
|
},
|
|
381
362
|
.None => ()
|
|
382
363
|
);
|
|
383
|
-
};
|
|
364
|
+
});
|
|
384
365
|
new_segments.push(name);
|
|
385
|
-
|
|
386
|
-
return Self(_segments: new_segments, _is_absolute: self._is_absolute);
|
|
366
|
+
return(Self(_segments : new_segments, _is_absolute : self._is_absolute));
|
|
387
367
|
}),
|
|
388
|
-
|
|
389
368
|
/// Check if this path starts with the given base path (segment-wise).
|
|
390
|
-
starts_with : (fn(self: Self, base: Self) -> bool)({
|
|
369
|
+
starts_with : (fn(self : Self, base : Self) -> bool)({
|
|
391
370
|
// Absolute vs relative mismatch
|
|
392
371
|
cond(
|
|
393
372
|
(self._is_absolute != base._is_absolute) => {
|
|
394
|
-
return
|
|
373
|
+
return(false);
|
|
395
374
|
},
|
|
396
375
|
true => ()
|
|
397
376
|
);
|
|
398
|
-
|
|
399
377
|
// Base has more segments than self
|
|
400
378
|
cond(
|
|
401
379
|
(base._segments.len() > self._segments.len()) => {
|
|
402
|
-
return
|
|
380
|
+
return(false);
|
|
403
381
|
},
|
|
404
382
|
true => ()
|
|
405
383
|
);
|
|
406
|
-
|
|
407
384
|
// Check if all base segments match
|
|
408
385
|
i := usize(0);
|
|
409
|
-
while
|
|
410
|
-
(i = (i + usize(1))),
|
|
411
|
-
{
|
|
386
|
+
while(i < base._segments.len(), i = (i + usize(1)), {
|
|
412
387
|
self_seg_opt := self._segments.get(i);
|
|
413
388
|
base_seg_opt := base._segments.get(i);
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
.Some(self_seg) => match(
|
|
389
|
+
match(
|
|
390
|
+
self_seg_opt,
|
|
391
|
+
.Some(self_seg) => match(
|
|
392
|
+
base_seg_opt,
|
|
417
393
|
.Some(base_seg) => {
|
|
418
394
|
cond(
|
|
419
395
|
(self_seg == base_seg) => (),
|
|
420
396
|
true => {
|
|
421
|
-
return
|
|
397
|
+
return(false);
|
|
422
398
|
}
|
|
423
399
|
);
|
|
424
400
|
},
|
|
425
401
|
.None => {
|
|
426
|
-
return
|
|
402
|
+
return(false);
|
|
427
403
|
}
|
|
428
404
|
),
|
|
429
405
|
.None => {
|
|
430
|
-
return
|
|
406
|
+
return(false);
|
|
431
407
|
}
|
|
432
408
|
);
|
|
433
|
-
};
|
|
434
|
-
|
|
435
|
-
return true;
|
|
409
|
+
});
|
|
410
|
+
return(true);
|
|
436
411
|
}),
|
|
437
|
-
|
|
438
412
|
/// Check if this path ends with the given suffix path (segment-wise).
|
|
439
|
-
ends_with : (fn(self: Self, suffix: Self) -> bool)({
|
|
413
|
+
ends_with : (fn(self : Self, suffix : Self) -> bool)({
|
|
440
414
|
// Suffix has more segments than self
|
|
441
415
|
cond(
|
|
442
416
|
(suffix._segments.len() > self._segments.len()) => {
|
|
443
|
-
return
|
|
417
|
+
return(false);
|
|
444
418
|
},
|
|
445
419
|
true => ()
|
|
446
420
|
);
|
|
447
|
-
|
|
448
421
|
// If suffix is absolute, it must match exactly
|
|
449
422
|
cond(
|
|
450
423
|
(suffix._is_absolute) => {
|
|
451
424
|
cond(
|
|
452
425
|
(self._is_absolute != suffix._is_absolute) => {
|
|
453
|
-
return
|
|
426
|
+
return(false);
|
|
454
427
|
},
|
|
455
428
|
true => ()
|
|
456
429
|
);
|
|
457
430
|
cond(
|
|
458
431
|
(self._segments.len() != suffix._segments.len()) => {
|
|
459
|
-
return
|
|
432
|
+
return(false);
|
|
460
433
|
},
|
|
461
434
|
true => ()
|
|
462
435
|
);
|
|
463
436
|
},
|
|
464
437
|
true => ()
|
|
465
438
|
);
|
|
466
|
-
|
|
467
439
|
// Check if suffix segments match the end of self
|
|
468
440
|
suffix_len := suffix._segments.len();
|
|
469
441
|
self_len := self._segments.len();
|
|
470
442
|
offset := (self_len - suffix_len);
|
|
471
|
-
|
|
472
443
|
i := usize(0);
|
|
473
|
-
while
|
|
474
|
-
|
|
475
|
-
{
|
|
476
|
-
self_seg_opt := self._segments.get((offset + i));
|
|
444
|
+
while(i < suffix_len, i = (i + usize(1)), {
|
|
445
|
+
self_seg_opt := self._segments.get(offset + i);
|
|
477
446
|
suffix_seg_opt := suffix._segments.get(i);
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
.Some(self_seg) => match(
|
|
447
|
+
match(
|
|
448
|
+
self_seg_opt,
|
|
449
|
+
.Some(self_seg) => match(
|
|
450
|
+
suffix_seg_opt,
|
|
481
451
|
.Some(suffix_seg) => {
|
|
482
452
|
cond(
|
|
483
453
|
(self_seg == suffix_seg) => (),
|
|
484
454
|
true => {
|
|
485
|
-
return
|
|
455
|
+
return(false);
|
|
486
456
|
}
|
|
487
457
|
);
|
|
488
458
|
},
|
|
489
459
|
.None => {
|
|
490
|
-
return
|
|
460
|
+
return(false);
|
|
491
461
|
}
|
|
492
462
|
),
|
|
493
463
|
.None => {
|
|
494
|
-
return
|
|
464
|
+
return(false);
|
|
495
465
|
}
|
|
496
466
|
);
|
|
497
|
-
};
|
|
498
|
-
|
|
499
|
-
return true;
|
|
467
|
+
});
|
|
468
|
+
return(true);
|
|
500
469
|
}),
|
|
501
|
-
|
|
502
470
|
/// Get all path components as a list. Absolute paths start with `"/"`.
|
|
503
|
-
components : (fn(self: Self) -> ArrayList(String))({
|
|
504
|
-
result := ArrayList(String).with_capacity(
|
|
505
|
-
|
|
471
|
+
components : (fn(self : Self) -> ArrayList(String))({
|
|
472
|
+
result := ArrayList(String).with_capacity(self._segments.len() + usize(1));
|
|
506
473
|
// Add root component if absolute
|
|
507
474
|
cond(
|
|
508
475
|
(self._is_absolute) => {
|
|
@@ -510,151 +477,149 @@ impl(Path,
|
|
|
510
477
|
},
|
|
511
478
|
true => ()
|
|
512
479
|
);
|
|
513
|
-
|
|
514
480
|
// Add all segments
|
|
515
481
|
i := usize(0);
|
|
516
|
-
while
|
|
517
|
-
(i = (i + usize(1))),
|
|
518
|
-
{
|
|
482
|
+
while(i < self._segments.len(), i = (i + usize(1)), {
|
|
519
483
|
seg := self._segments.get(i);
|
|
520
|
-
match(
|
|
484
|
+
match(
|
|
485
|
+
seg,
|
|
521
486
|
.Some(s) => {
|
|
522
487
|
result.push(s);
|
|
523
488
|
},
|
|
524
489
|
.None => ()
|
|
525
490
|
);
|
|
526
|
-
};
|
|
527
|
-
|
|
528
|
-
return result;
|
|
491
|
+
});
|
|
492
|
+
return(result);
|
|
529
493
|
}),
|
|
530
|
-
|
|
531
494
|
/// Check if the path is absolute.
|
|
532
|
-
is_absolute : (fn(self: Self) -> bool)(
|
|
495
|
+
is_absolute : (fn(self : Self) -> bool)(
|
|
533
496
|
self._is_absolute
|
|
534
497
|
),
|
|
535
|
-
|
|
536
498
|
/// Check if the path is relative (not absolute).
|
|
537
|
-
is_relative : (fn(self: Self) -> bool)(
|
|
538
|
-
!self._is_absolute
|
|
499
|
+
is_relative : (fn(self : Self) -> bool)(
|
|
500
|
+
!(self._is_absolute)
|
|
539
501
|
)
|
|
540
502
|
);
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
503
|
+
impl(
|
|
504
|
+
Path,
|
|
505
|
+
ToString(
|
|
506
|
+
to_string : (
|
|
507
|
+
self -> {
|
|
508
|
+
segments := self._segments;
|
|
509
|
+
sep := String.from("/");
|
|
510
|
+
result := String.new();
|
|
511
|
+
// Check if first segment is a Windows drive letter (e.g., "C:")
|
|
512
|
+
has_drive_letter := cond(
|
|
513
|
+
(segments.len() > usize(0)) => {
|
|
514
|
+
first_seg_opt := segments.get(usize(0));
|
|
515
|
+
match(
|
|
516
|
+
first_seg_opt,
|
|
517
|
+
.Some(first_seg) =>
|
|
518
|
+
// Check if it's a drive letter like "C:"
|
|
519
|
+
cond(
|
|
520
|
+
(first_seg.len() == usize(2)) => {
|
|
521
|
+
b0_opt := first_seg.as_bytes().get(usize(0));
|
|
522
|
+
b1_opt := first_seg.as_bytes().get(usize(1));
|
|
523
|
+
match(
|
|
524
|
+
b0_opt,
|
|
525
|
+
.Some(b0) => match(
|
|
526
|
+
b1_opt,
|
|
527
|
+
.Some(b1) => {
|
|
528
|
+
is_letter := (((b0 >= u8(65)) && (b0 <= u8(90))) || ((b0 >= u8(97)) && (b0 <= u8(122))));
|
|
529
|
+
is_colon := (b1 == u8(58));
|
|
530
|
+
is_letter && is_colon
|
|
531
|
+
},
|
|
532
|
+
.None => false
|
|
533
|
+
),
|
|
534
|
+
.None => false
|
|
535
|
+
)
|
|
536
|
+
},
|
|
537
|
+
true => false
|
|
538
|
+
),
|
|
539
|
+
.None => false
|
|
540
|
+
)
|
|
541
|
+
},
|
|
542
|
+
true => false
|
|
543
|
+
);
|
|
544
|
+
// For Unix absolute paths, prepend /
|
|
545
|
+
// For Windows paths with drive letter, don't prepend /
|
|
546
|
+
cond(
|
|
547
|
+
(self._is_absolute && !(has_drive_letter)) => {
|
|
548
|
+
result = result.concat(sep);
|
|
549
|
+
},
|
|
550
|
+
true => ()
|
|
551
|
+
);
|
|
552
|
+
i := usize(0);
|
|
553
|
+
segments_len := segments.len();
|
|
554
|
+
while(i < segments_len, i = (i + usize(1)), {
|
|
555
|
+
seg := segments.get(i);
|
|
556
|
+
match(
|
|
557
|
+
seg,
|
|
558
|
+
.Some(s) => {
|
|
559
|
+
result = result.concat(s);
|
|
560
|
+
cond(
|
|
561
|
+
(i < (segments_len - usize(1))) => {
|
|
562
|
+
result = result.concat(sep);
|
|
563
|
+
},
|
|
564
|
+
true => ()
|
|
565
|
+
);
|
|
599
566
|
},
|
|
600
|
-
|
|
567
|
+
.None => ()
|
|
601
568
|
);
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
true => ()
|
|
618
|
-
);
|
|
619
|
-
|
|
620
|
-
// Compare segment lengths
|
|
621
|
-
cond(
|
|
622
|
-
(self._segments.len() != other._segments.len()) => {
|
|
623
|
-
return false;
|
|
624
|
-
},
|
|
625
|
-
true => ()
|
|
626
|
-
);
|
|
627
|
-
|
|
628
|
-
// Compare each segment
|
|
629
|
-
i := usize(0);
|
|
630
|
-
while ((i < self._segments.len())),
|
|
631
|
-
(i = (i + usize(1))),
|
|
632
|
-
{
|
|
633
|
-
self_seg_opt := self._segments.get(i);
|
|
634
|
-
other_seg_opt := other._segments.get(i);
|
|
635
|
-
|
|
636
|
-
match(self_seg_opt,
|
|
637
|
-
.Some(self_seg) => match(other_seg_opt,
|
|
638
|
-
.Some(other_seg) => {
|
|
639
|
-
cond(
|
|
640
|
-
(self_seg == other_seg) => (),
|
|
641
|
-
true => {
|
|
642
|
-
return false;
|
|
643
|
-
}
|
|
644
|
-
);
|
|
569
|
+
});
|
|
570
|
+
return(result);
|
|
571
|
+
}
|
|
572
|
+
)
|
|
573
|
+
)
|
|
574
|
+
);
|
|
575
|
+
impl(
|
|
576
|
+
Path,
|
|
577
|
+
Eq(Path)(
|
|
578
|
+
(==) : (
|
|
579
|
+
(self, other) -> {
|
|
580
|
+
// Compare absolute vs relative
|
|
581
|
+
cond(
|
|
582
|
+
(self._is_absolute != other._is_absolute) => {
|
|
583
|
+
return(false);
|
|
645
584
|
},
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
)
|
|
659
|
-
|
|
660
|
-
|
|
585
|
+
true => ()
|
|
586
|
+
);
|
|
587
|
+
// Compare segment lengths
|
|
588
|
+
cond(
|
|
589
|
+
(self._segments.len() != other._segments.len()) => {
|
|
590
|
+
return(false);
|
|
591
|
+
},
|
|
592
|
+
true => ()
|
|
593
|
+
);
|
|
594
|
+
// Compare each segment
|
|
595
|
+
i := usize(0);
|
|
596
|
+
while(i < self._segments.len(), i = (i + usize(1)), {
|
|
597
|
+
self_seg_opt := self._segments.get(i);
|
|
598
|
+
other_seg_opt := other._segments.get(i);
|
|
599
|
+
match(
|
|
600
|
+
self_seg_opt,
|
|
601
|
+
.Some(self_seg) => match(
|
|
602
|
+
other_seg_opt,
|
|
603
|
+
.Some(other_seg) => {
|
|
604
|
+
cond(
|
|
605
|
+
(self_seg == other_seg) => (),
|
|
606
|
+
true => {
|
|
607
|
+
return(false);
|
|
608
|
+
}
|
|
609
|
+
);
|
|
610
|
+
},
|
|
611
|
+
.None => {
|
|
612
|
+
return(false);
|
|
613
|
+
}
|
|
614
|
+
),
|
|
615
|
+
.None => {
|
|
616
|
+
return(false);
|
|
617
|
+
}
|
|
618
|
+
);
|
|
619
|
+
});
|
|
620
|
+
return(true);
|
|
621
|
+
}
|
|
622
|
+
)
|
|
623
|
+
)
|
|
624
|
+
);
|
|
625
|
+
export(Path);
|