File: iter.ha

package info (click to toggle)
hare 0.25.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,948 kB
  • sloc: asm: 1,264; makefile: 123; sh: 114; lisp: 101
file content (118 lines) | stat: -rw-r--r-- 3,482 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// SPDX-License-Identifier: MPL-2.0
// (c) Hare authors <https://harelang.org>

use bytes;
use strings;

export type iterator = struct {
	cur: []u8,
	reverse: bool,
};

// 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,
};

// 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. 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 done if none
// remain. Advances the iterator.
export fn nextiter(it: *iterator) (str | done) = {
	if (len(it.cur) == 0) return done;
	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, SEP)) {
		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, SEP)) {
		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]);
		};
	};
};

// Gets 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(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 done);
	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 done);

	set(&buf, local("foo/bar/baz")!)!;
	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 done);
	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 done);

	set(&buf, "foo")!;
	i = iter(&buf);
	assert(nextiter(&i) as str == "foo");
	assert(nextiter(&i) is done);
	i = riter(&buf);
	assert(nextiter(&i) as str == "foo");
	assert(nextiter(&i) is done);

	set(&buf, local("/")!)!;
	i = iter(&buf);
	assert(nextiter(&i) as str == local("/")!);
	assert(nextiter(&i) is done);
	i = riter(&buf);
	assert(nextiter(&i) as str == local("/")!);
	assert(nextiter(&i) is done);
};