commit 2183c5e7d1ccf5e9b42b10b617312ef0211f1349
parent b88e9ece05c5fe935b7cc9bc5ef43e38b4f1dc77
Author: Autumn! <autumnull@posteo.net>
Date: Sun, 7 May 2023 01:43:34 +0000
path: refactor iterator, add riter() and remaining()
Signed-off-by: Autumn! <autumnull@posteo.net>
Diffstat:
M | fs/util.ha | | | 2 | +- |
M | path/iter.ha | | | 143 | +++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------- |
2 files changed, 94 insertions(+), 51 deletions(-)
diff --git a/fs/util.ha b/fs/util.ha
@@ -149,7 +149,7 @@ export fn realpath(fs: *fs, path: str) (str | error) = {
path::set(&pathbuf, path)!;
const iter = path::iter(&pathbuf);
for (true) {
- const item = match (path::next(&iter)) {
+ const item = match (path::nextiter(&iter)) {
case let item: str =>
yield item;
case void =>
diff --git a/path/iter.ha b/path/iter.ha
@@ -5,74 +5,117 @@
use bytes;
use strings;
-export type iflags = enum uint {
- NONE = 0,
- ABSOLUTE = 1 << 0,
-};
-
export type iterator = struct {
- tok: bytes::tokenizer,
- flags: iflags,
+ cur: []u8,
+ reverse: bool,
};
let pathsep: []u8 = [PATHSEP];
-// Returns an iterator which yields each component of a path. If the path is
-// absolute, the first component will be the root path (e.g. "/").
-export fn iter(buf: *buffer) iterator = {
- let path = buf.buf[..buf.end];
- let flags = iflags::NONE;
- if (len(path) > 0 && path[0] == PATHSEP) {
- flags |= iflags::ABSOLUTE;
- path = path[1..];
- };
- if (len(path) > 1 && path[len(path) - 1] == PATHSEP) {
- path = path[..len(path) - 1];
- };
+// Returns an [[iterator]] which yields each component of a path, moving down
+// through child dirs. If the path is absolute, the first component will be
+// the root. The iterator can be copied to save its state.
+export fn iter(buf: *buffer) iterator = iterator {
+ cur = buf.buf[..buf.end],
+ reverse = false,
+};
- return iterator {
- tok = bytes::tokenize(path, pathsep),
- flags = flags,
- };
+// Returns an [[iterator]] which yields each component of a path, moving up
+// through parent dirs. If the path is absolute, the last component will be
+// the root. The iterator can be copied to save its state.
+export fn riter(buf: *buffer) iterator = iterator {
+ cur = buf.buf[..buf.end],
+ reverse = true,
};
-// Returns the next path component from an iterator, or void if none remain.
-export fn next(iter: *iterator) (str | void) = {
- if (iter.flags & iflags::ABSOLUTE == iflags::ABSOLUTE) {
- iter.flags &= ~iflags::ABSOLUTE;
- static assert(PATHSEP <= 0x7F);
- return strings::fromutf8_unsafe(pathsep);
- };
- match (bytes::next_token(&iter.tok)) {
- case let b: []u8 =>
- return strings::fromutf8_unsafe(b);
- case void => void;
+
+// Returns the next path component from an [[iterator]], or void if none
+// remain. Does not advance the iterator.
+export fn peekiter(it: *iterator) (str | void) = {
+ if (len(it.cur) == 0) return void;
+ const (result, remaining) = split_iter(it);
+ return strings::fromutf8_unsafe(result);
+};
+
+// Returns the next path component from an [[iterator]], or void if none
+// remain. Advances the iterator.
+export fn nextiter(it: *iterator) (str | void) = {
+ if (len(it.cur) == 0) return void;
+ const (result, remaining) = split_iter(it);
+ it.cur = remaining;
+ return strings::fromutf8_unsafe(result);
+};
+
+// helper function for nextiter and peekiter, returns (result, remaining)
+fn split_iter(it: *iterator) ([]u8, []u8) = {
+ if (it.reverse) {
+ match (bytes::rindex(it.cur, PATHSEP)) {
+ case let sep: size =>
+ let res = it.cur[sep+1..];
+ if (sep == 0) {
+ if (len(it.cur) == 1) {
+ res = it.cur; // return the root dir
+ } else {
+ sep = 1; // leave the root for next
+ };
+ };
+ return (res, it.cur[..sep]);
+ case void =>
+ return (it.cur, it.cur[..0]);
+ };
+ } else {
+ match (bytes::index(it.cur, PATHSEP)) {
+ case let i: size =>
+ return (it.cur[..if (i == 0) 1 else i], it.cur[i+1..]);
+ case void =>
+ return (it.cur, it.cur[..0]);
+ };
};
};
+// get the remaining path from an iterator, without advancing the iterator.
+export fn iterrem(it: *iterator) str = strings::fromutf8_unsafe(it.cur);
+
@test fn iter() void = {
const buf = init(local("/foo/bar/baz"))!;
let i = iter(&buf);
- assert(next(&i) as str == local("/"));
- assert(next(&i) as str == "foo");
- assert(next(&i) as str == "bar");
- assert(next(&i) as str == "baz");
- assert(next(&i) is void);
+ assert(nextiter(&i) as str == local("/"));
+ assert(nextiter(&i) as str == "foo");
+ assert(nextiter(&i) as str == "bar");
+ assert(nextiter(&i) as str == "baz");
+ assert(nextiter(&i) is void);
+ i = riter(&buf);
+ assert(nextiter(&i) as str == "baz");
+ assert(nextiter(&i) as str == "bar");
+ assert(nextiter(&i) as str == "foo");
+ assert(nextiter(&i) as str == local("/"));
+ assert(nextiter(&i) is void);
set(&buf, local("foo/bar/baz"))!;
- let i = iter(&buf);
- assert(next(&i) as str == "foo");
- assert(next(&i) as str == "bar");
- assert(next(&i) as str == "baz");
- assert(next(&i) is void);
+ i = iter(&buf);
+ assert(nextiter(&i) as str == "foo");
+ assert(nextiter(&i) as str == "bar");
+ assert(nextiter(&i) as str == "baz");
+ assert(nextiter(&i) is void);
+ i = riter(&buf);
+ assert(nextiter(&i) as str == "baz");
+ assert(nextiter(&i) as str == "bar");
+ assert(nextiter(&i) as str == "foo");
+ assert(nextiter(&i) is void);
set(&buf, "foo")!;
- let i = iter(&buf);
- assert(next(&i) as str == "foo");
- assert(next(&i) is void);
+ i = iter(&buf);
+ assert(nextiter(&i) as str == "foo");
+ assert(nextiter(&i) is void);
+ i = riter(&buf);
+ assert(nextiter(&i) as str == "foo");
+ assert(nextiter(&i) is void);
set(&buf, local("/"))!;
- let i = iter(&buf);
- assert(next(&i) as str == local("/"));
- assert(next(&i) is void);
+ i = iter(&buf);
+ assert(nextiter(&i) as str == local("/"));
+ assert(nextiter(&i) is void);
+ i = riter(&buf);
+ assert(nextiter(&i) as str == local("/"));
+ assert(nextiter(&i) is void);
};