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 };