Hare by Example: Directories

Like file paths, Hare has good support for working with directories.

use io;
use fmt;
use fs;
use strings;
use os;

// walkfunc defines a callback we will run for every directory entry we
// see so the caller can use it.
type walkfunc = fn(dir: str, entry: fs::dirent) void;

// walk takes a root directory and a callback function and will traverse
// down into each sub-directory it finds while also calling the callback
// with each directory entry.
fn walk(dir: str, cb: *walkfunc) (void | fs::error | nomem) = {
	const entries = os::readdir(dir)?;
	defer fs::dirents_free(entries);

	for (let entry .. entries) {
		cb(dir, entry);

		if (fs::isdir(entry.ftype)) {
			const sub = strings::concat(dir, "/",
				entry.name)!;
			walk(sub, cb)?;
		};
	};
};

// walkcb is our specified walkfunc we want to execute for each
// directory entry.
fn walkcb(dir: str, entry: fs::dirent) void = {
	const path = strings::concat(dir, "/", entry.name)!;
	defer free(path);

	fmt::printf("WALK - path: {}, file: {}, dir: {}\n", path,
		fs::isfile(entry.ftype),
		fs::isdir(entry.ftype))!;
};

export fn main() void = {
	// We can make a directory and defer deleting it when we're
	// finished.
	os::mkdir("/tmp/new-dir", 0o755)!;
	defer os::rmdirall("/tmp/new-dir")!;

	// Lets make some more sub-directories to create some hierarchy.
	os::mkdir("/tmp/new-dir/sub1", 0o755)!;
	os::mkdir("/tmp/new-dir/sub2", 0o755)!;

	// Now we can add some files inside of each of the directories.
	const f1 = os::create("/tmp/new-dir/file.txt", 0o644)!;
	defer io::close(f1)!;

	const f1 = os::create("/tmp/new-dir/sub1/cool.txt", 0o644)!;
	defer io::close(f1)!;

	const f1 = os::create("/tmp/new-dir/sub2/l33t.txt", 0o644)!;
	defer io::close(f1)!;

	// We can open the directory we created and get the entries
	// inside of it and check whether they are files or directories.
	const entries = os::readdir("/tmp/new-dir")!;
	defer fs::dirents_free(entries);

	for (let entry .. entries) {
		fmt::printf("name: {}, file: {}, dir: {}\n", entry.name,
			fs::isfile(entry.ftype),
			fs::isdir(entry.ftype))!;
	};

	// Hare does not have built-in support for walking a directory
	// at a specified root, but we can implement a basic version of
	// this ourselves. You may also consider using an iterator for
	// more a efficient method.
	walk("/tmp/new-dir", &walkcb)!;
};