hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

iter.ha (3475B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use bytes;
      5 use strings;
      6 
      7 export type iterator = struct {
      8 	cur: []u8,
      9 	reverse: bool,
     10 };
     11 
     12 // Returns an [[iterator]] which yields each component of a path, moving down
     13 // through child dirs. If the path is absolute, the first component will be
     14 // the root. The iterator can be copied to save its state.
     15 export fn iter(buf: *buffer) iterator = iterator {
     16 	cur = buf.buf[..buf.end],
     17 	reverse = false,
     18 };
     19 
     20 // Returns an [[iterator]] which yields each component of a path, moving up
     21 // through parent dirs. If the path is absolute, the last component will be
     22 // the root. The iterator can be copied to save its state.
     23 export fn riter(buf: *buffer) iterator = iterator {
     24 	cur = buf.buf[..buf.end],
     25 	reverse = true,
     26 };
     27 
     28 
     29 // Returns the next path component from an [[iterator]], or void if none
     30 // remain. Does not advance the iterator.
     31 export fn peekiter(it: *iterator) (str | void) = {
     32 	if (len(it.cur) == 0) return void;
     33 	const (result, remaining) = split_iter(it);
     34 	return strings::fromutf8_unsafe(result);
     35 };
     36 
     37 // Returns the next path component from an [[iterator]], or void if none
     38 // remain. Advances the iterator.
     39 export fn nextiter(it: *iterator) (str | void) = {
     40 	if (len(it.cur) == 0) return void;
     41 	const (result, remaining) = split_iter(it);
     42 	it.cur = remaining;
     43 	return strings::fromutf8_unsafe(result);
     44 };
     45 
     46 // helper function for nextiter and peekiter, returns (result, remaining)
     47 fn split_iter(it: *iterator) ([]u8, []u8) = {
     48 	if (it.reverse) {
     49 		match (bytes::rindex(it.cur, SEP)) {
     50 		case let sep: size =>
     51 			let res = it.cur[sep+1..];
     52 			if (sep == 0) {
     53 				if (len(it.cur) == 1) {
     54 					res = it.cur; // return the root dir
     55 				} else {
     56 					sep = 1; // leave the root for next
     57 				};
     58 			};
     59 			return (res, it.cur[..sep]);
     60 		case void =>
     61 			return (it.cur, it.cur[..0]);
     62 		};
     63 	} else {
     64 		match (bytes::index(it.cur, SEP)) {
     65 		case let i: size =>
     66 			return (it.cur[..if (i == 0) 1 else i], it.cur[i+1..]);
     67 		case void =>
     68 			return (it.cur, it.cur[..0]);
     69 		};
     70 	};
     71 };
     72 
     73 // Gets the remaining path from an iterator, without advancing the iterator.
     74 export fn iterrem(it: *iterator) str = strings::fromutf8_unsafe(it.cur);
     75 
     76 @test fn iter() void = {
     77 	const buf = init(local("/foo/bar/baz"))!;
     78 	let i = iter(&buf);
     79 	assert(nextiter(&i) as str == local("/"));
     80 	assert(nextiter(&i) as str == "foo");
     81 	assert(nextiter(&i) as str == "bar");
     82 	assert(nextiter(&i) as str == "baz");
     83 	assert(nextiter(&i) is void);
     84 	i = riter(&buf);
     85 	assert(nextiter(&i) as str == "baz");
     86 	assert(nextiter(&i) as str == "bar");
     87 	assert(nextiter(&i) as str == "foo");
     88 	assert(nextiter(&i) as str == local("/"));
     89 	assert(nextiter(&i) is void);
     90 
     91 	set(&buf, local("foo/bar/baz"))!;
     92 	i = iter(&buf);
     93 	assert(nextiter(&i) as str == "foo");
     94 	assert(nextiter(&i) as str == "bar");
     95 	assert(nextiter(&i) as str == "baz");
     96 	assert(nextiter(&i) is void);
     97 	i = riter(&buf);
     98 	assert(nextiter(&i) as str == "baz");
     99 	assert(nextiter(&i) as str == "bar");
    100 	assert(nextiter(&i) as str == "foo");
    101 	assert(nextiter(&i) is void);
    102 
    103 	set(&buf, "foo")!;
    104 	i = iter(&buf);
    105 	assert(nextiter(&i) as str == "foo");
    106 	assert(nextiter(&i) is void);
    107 	i = riter(&buf);
    108 	assert(nextiter(&i) as str == "foo");
    109 	assert(nextiter(&i) is void);
    110 
    111 	set(&buf, local("/"))!;
    112 	i = iter(&buf);
    113 	assert(nextiter(&i) as str == local("/"));
    114 	assert(nextiter(&i) is void);
    115 	i = riter(&buf);
    116 	assert(nextiter(&i) as str == local("/"));
    117 	assert(nextiter(&i) is void);
    118 };