hare

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

walk.ha (2291B)


      1 // License: MPL-2.0
      2 // (c) 2021-2022 Drew DeVault <sir@cmpwn.com>
      3 // (c) 2021 Ember Sawady <ecs@d2evs.net>
      4 use errors;
      5 use fs;
      6 use hare::ast;
      7 use path;
      8 use strings;
      9 
     10 // Recursively scans the filesystem to find valid Hare modules for the given
     11 // [[context]], given the path to the entry point. The caller must free the
     12 // return value with [[walk_free]].
     13 export fn walk(ctx: *context, path: str) ([]ast::ident | error) = {
     14 	let items: []ast::ident = [];
     15 	_walk(ctx, path, &items, [])?;
     16 	return items;
     17 };
     18 
     19 fn _walk(
     20 	ctx: *context,
     21 	path: str,
     22 	items: *[]ast::ident,
     23 	ns: ast::ident,
     24 ) (void | error) = {
     25 	match (scan(ctx, path)) {
     26 	case error =>
     27 		void;
     28 	case let ver: version =>
     29 		append(items, ns);
     30 	};
     31 
     32 	let iter = match (fs::iter(ctx.fs, path)) {
     33 	case fs::wrongtype =>
     34 		return; // Single file "module"
     35 	case let err: fs::error =>
     36 		return err;
     37 	case let iter: *fs::iterator =>
     38 		yield iter;
     39 	};
     40 	defer fs::finish(iter);
     41 
     42 	// TODO: Refactor me to use path::buffer
     43 	for (true) {
     44 		const ent = match (fs::next(iter)) {
     45 		case void =>
     46 			break;
     47 		case let ent: fs::dirent =>
     48 			yield ent;
     49 		};
     50 
     51 		if (strings::hasprefix(ent.name, "+")
     52 				|| strings::hasprefix(ent.name, "-")
     53 				|| strings::hasprefix(ent.name, ".")) {
     54 			continue;
     55 		};
     56 
     57 		switch (ent.ftype) {
     58 		case fs::mode::DIR =>
     59 			// TODO: Test that this is a valid name (grammar)
     60 			let subpath = path::init(path, ent.name)!;
     61 			let newns = ast::ident_dup(ns);
     62 			append(newns, strings::dup(ent.name));
     63 			_walk(ctx, path::string(&subpath), items, newns)?;
     64 		case fs::mode::LINK =>
     65 			let linkbuf = path::init(path, ent.name)!;
     66 			path::set(&linkbuf, fs::readlink(ctx.fs, path::string(&linkbuf))?)!;
     67 			if (!path::abs(&linkbuf)) {
     68 				path::prepend(&linkbuf, path)!;
     69 			};
     70 
     71 			const st = fs::stat(ctx.fs, path::string(&linkbuf))?;
     72 			if (fs::isdir(st.mode)) {
     73 				let subpath = path::init(path, ent.name)!;
     74 				let newns = ast::ident_dup(ns);
     75 				append(newns, strings::dup(ent.name));
     76 				_walk(ctx, path::string(&subpath), items, newns)?;
     77 			};
     78 		case fs::mode::REG =>
     79 			void; // no-op
     80 		case => abort();
     81 		};
     82 	};
     83 };
     84 
     85 // Frees resources associated with the return value of [[walk]].
     86 export fn walk_free(items: []ast::ident) void = {
     87 	for (let i = 0z; i < len(items); i += 1) {
     88 		ast::ident_free(items[i]);
     89 	};
     90 	free(items);
     91 };