hare

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

commit c86cf0f40804bbbc02c1d2d8496b8b20535aa816
parent b2f00d7ab64d1058f3f5be593563d096b4e0d710
Author: Drew DeVault <sir@cmpwn.com>
Date:   Wed, 24 Feb 2021 11:25:41 -0500

fs: add iterator details

Diffstat:
Mfs/fs.ha | 9++++++++-
Mfs/types.ha | 28+++++++++++++++++++++++++---
Mfs/util.ha | 13+++++++++++++
3 files changed, 46 insertions(+), 4 deletions(-)

diff --git a/fs/fs.ha b/fs/fs.ha @@ -25,7 +25,8 @@ export fn create(fs: *fs, path: path, mode: io::mode) (*io::stream | error) = { }; // Returns an iterator for a path, which yields the contents of a directory. -// Pass empty string to yield from the root. +// Pass empty string to yield from the root. The order in which entries are +// returned is undefined. export fn iter(fs: *fs, path: path) (*iterator | error) = { return match (fs.iter) { null => io::unsupported, @@ -49,3 +50,9 @@ export fn subdir(fs: *fs, path: path) (*fs | error) = { f: *subdirfunc => f(fs, path), }; }; + +// Returns the next directory entry from an interator, or void if none remain. +// +// Use [stat_dup] if you need to keep the filestat around for longer than the +// next call to [next]. +export fn next(iter: *iterator) (filestat | void) = iter.next(iter); diff --git a/fs/types.ha b/fs/types.ha @@ -1,4 +1,5 @@ use io; +use strings; // A path or path component. export type path = (str | []u8); @@ -81,9 +82,14 @@ export type stat_mask = enum uint { }; // Information about a file or directory. The mask field defines what other -// fields are set; mode is always set. +// fields are set; mode and path are always set. +// +// The path string is typically borrowed from the filesystem's internal state; +// if you want to keep this around for any length of time you should use +// [stat_dup]. export type filestat = struct { mask: stat_mask, + path: str, mode: mode, uid: uint, gid: uint, @@ -91,6 +97,16 @@ export type filestat = struct { // TODO: atime et al }; +// Duplicates a [filestat] object. Call [stat_free] to get rid of it later. +export fn stat_dup(f: filestat) filestat = { + let new = f; + new.path = strings::dup(new.path); + return new; +}; + +// Frees a [filestat] object which was duplicated with [stat_dup]. +export fn stat_free(f: filestat) void = free(f.path); + export type closefunc = fn(fs: *fs) void; export type openfunc = fn(fs: *fs, path: path, mode: io::mode) (*io::stream | error); export type createfunc = fn(fs: *fs, path: path, mode: io::mode) (*io::stream | error); @@ -111,6 +127,9 @@ export type fs = struct { // Returns an iterator for a path, which yields the contents of a // directory. Pass empty string to yield from the root. + // + // The iterator must return all entries without error. If an error would + // occur, it should be identified here and returned upfront. iter: nullable *iterfunc, // Obtains information about a file or directory. If the target is a @@ -121,7 +140,10 @@ export type fs = struct { subdir: nullable *subdirfunc, }; -// TODO +export type nextfunc = fn(iter: *iterator) (filestat | void); + export type iterator = struct { - placeholder: int, + // Returns the next member of the directory, or void if there are none + // remaining. + next: *nextfunc, }; diff --git a/fs/util.ha b/fs/util.ha @@ -33,3 +33,16 @@ export fn is_link(mode: mode) bool = mode & mode::LINK == mode::LINK; // Returns true if this item is a Unix socket. export fn is_socket(mode: mode) bool = mode & mode::SOCK == mode::SOCK; + +// Reads all entries from a directory. +export fn readdir(fs: *fs, path: path) ([]filestat | error) = { + let i = iter(fs, path)?; + let ents: []filestat = []; + for (true) { + match (next(i)) { + f: filestat => append(ents, stat_dup(f)), + void => break, + }; + }; + return ents; +};