Skip to content

Commit

Permalink
Add Path.list_all
Browse files Browse the repository at this point in the history
This method returns an iterator that recursively yields the contents of
directories (excluding the directories themselves), using an unspecified
order.

Changelog: added
  • Loading branch information
yorickpeterse committed Jan 2, 2024
1 parent 83713aa commit 46b5fc2
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 1 deletion.
43 changes: 42 additions & 1 deletion std/src/std/fs/path.inko
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import std.cmp.Equal
import std.fmt.(Format, Formatter)
import std.fs.DirectoryEntry
import std.io.(Error, Size)
import std.iter.Iter
import std.iter.(Iter, Stream)
import std.libc.unix.dir.(ReadDirectory as ReadDirectoryInner) if unix
import std.string.(ToString, IntoString)
import std.sys
Expand Down Expand Up @@ -484,6 +484,47 @@ class pub Path {
}
}

# Returns an iterator that yields all non-directory entries in `self` and in
# any sub directories.
#
# The order in which entries are returned is unspecified and shouldn't be
# relied upon, and may change at any given point.
#
# If this iterator fails to read a sub directory (e.g. `bar` in `./foo/bar`
# isn't readable) a `Some(Error(std.io.Error))` is returned. Because a `Some`
# is returned the iterator can advance when encountering an error, similar to
# the iterator returned by `Path.list`.
#
# # Examples
#
# import std.fs.path.Path
#
# Path.new('/tmp').list_all.unwrap.next
# # => Option.Some(Result.Ok(DirectoryEntry { ... }))
fn pub list_all -> Result[Stream[Result[DirectoryEntry, Error]], Error] {
list.map fn (iter) {
let dirs = []
let mut current = iter

Stream.new fn move {
loop {
match current.next {
case Some(Ok({ @path = p, @type = Directory })) -> dirs.push(p)
case Some(Ok(entry)) -> return Option.Some(Result.Ok(entry))
case Some(Error(e)) -> return Option.Some(Result.Error(e))
case None -> match dirs.pop {
case Some(dir) -> match dir.list {
case Ok(iter) -> current = iter
case Error(e) -> return Option.Some(Result.Error(e))
}
case _ -> return Option.None
}
}
}
}
}
}

# Removes the file `self` points to.
#
# If `self` points to a directory, an error is returned.
Expand Down
35 changes: 35 additions & 0 deletions std/test/std/fs/test_path.inko
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,41 @@ fn pub tests(t: mut Tests) {
t.true(root.list.error?)
}

t.test('Path.list_recursive with a valid directory') fn (t) {
let root = env.temporary_directory.join("inko-test-dir-{t.id}")
let foo = root.join('foo')
let bar = foo.join('bar')
let baz = bar.join('baz')

baz.create_directory_all.unwrap
write('a', foo.join('a.txt'))
write('b', foo.join('b.txt'))
write('c', bar.join('c.txt'))
write('d', baz.join('d.txt'))

let paths = root
.list_all
.map fn (i) {
i
.select_map fn (r) { r.ok }
.map fn (e) { match e { case { @path = p } -> p } }
.to_array
}
.unwrap_or_else fn { [] }

t.equal(paths.size, 4)
t.true(paths.contains?(foo.join('a.txt')))
t.true(paths.contains?(foo.join('b.txt')))
t.true(paths.contains?(bar.join('c.txt')))
t.true(paths.contains?(baz.join('d.txt')))
}

t.test('Path.list_recursive with an invalid directory') fn (t) {
let root = env.temporary_directory.join("inko-test-dir-{t.id}")

t.true(root.list_all.error?)
}

t.test('Path.remove_file') fn (t) {
let path = env.temporary_directory.join("inko-test-{t.id}")

Expand Down

0 comments on commit 46b5fc2

Please sign in to comment.