z_mirror/hare-fmm
git clone https://git.raiguard.me/z_mirror/hare-fmm
74b341b
trunk
1use encoding::json; 2use fmt; 3use fs; 4use io; 5use os; 6use os::exec; 7use path; 8use strings; 9 10type modfile = struct { 11 dependencies: ([]dependency | void), 12 ftype: fs::mode, 13 ident: modident, 14 path: str, 15}; 16 17type modfile_error = (fs::error | version_error); 18 19// Frees resources related to a [[modfile]]. 20fn modfile_free(file: *modfile) void = { 21 free(file.path); 22 modident_free(&file.ident); 23 match (file.dependencies) { 24 case let deps: []dependency => 25 dependency_free_all(deps); 26 case => void; 27 }; 28}; 29 30// Constructs a [[modfile]] from a directory entry. The return value must be 31// freed with [[modfile_free]]. 32fn modfile_load( 33 path: str, 34 dirent: *fs::dirent 35) (modfile | void | modfile_error) = { 36 if (dirent.name == "." 37 || dirent.name == ".." 38 || dirent.name == "mod-list.json" 39 || dirent.name == "mod-settings.dat") 40 return void; 41 42 let filepath = path::init(path, dirent.name)!; 43 let file = modfile { 44 dependencies = void, 45 ftype = dirent.ftype, 46 ident = modident_parse(dirent.name), 47 path = strings::dup(path::string(&filepath))!, 48 }; 49 // Modfiles must have versions 50 if (file.ident.1 is void) { 51 if (!modfile_read_info(&file)) { 52 modfile_free(&file); 53 fmt::errorln("Unable to get info for", dirent.name)!; 54 return void; 55 }; 56 }; 57 58 return file; 59}; 60 61// Extracts and parses a modfile's info.json properties. 62fn modfile_read_info(mod: *modfile) bool = { 63 // Get info.json contents 64 const json = if (fs::isdir(mod.ftype) || fs::islink(mod.ftype)) { 65 const info_path = path::init(mod.path, "info.json")!; 66 let file = os::open(path::string(&info_path))!; 67 yield json::load(file)! as json::object; 68 } else if (fs::isfile(mod.ftype)) { 69 const info_path = path::init(mod.ident.0, "info.json")!; 70 // Extract info.json from zip file 71 let cmd = exec::cmd("unzip", "-p", mod.path, path::string(&info_path))!; 72 let pipe = exec::pipe(); 73 exec::addfile(&cmd, os::stdout_file, pipe.1)!; 74 let proc = exec::start(&cmd)!; 75 io::close(pipe.1)!; 76 77 let data = io::drain(pipe.0)!; 78 defer free(data); 79 io::close(pipe.0)!; 80 exec::wait(&proc)!; 81 82 yield json::loadstr(strings::fromutf8(data)!)! as json::object; 83 } else { 84 return false; 85 }; 86 defer json::finish(json); 87 88 // Ident 89 const name = json::get(&json, "name") as *json::value; 90 const name = *name as str; 91 const version = json::get(&json, "version") as *json::value; 92 const version = version_parse(*version as str)!; 93 modident_free(&mod.ident); 94 mod.ident = (strings::dup(name)!, version); 95 96 // Dependencies 97 mod.dependencies = match (json::get(&json, "dependencies")) { 98 case let deps: *json::value => 99 const deps = *deps as []json::value; 100 let output: []dependency = []; 101 for (let dep &.. deps) { 102 // TODO: Error handling 103 const dep_str = *dep as str; 104 append(output, dependency_parse(dep_str)!)!; 105 }; 106 yield output; 107 }; 108 109 return true; 110};