use encoding::json; use errors; use fmt; use fs; use io; use os; use path; use strings; // A mods directory, containing a mod-list.json and any number of zipped or // unzipped mods. type dir = struct { modfiles: []modfile, modlist: modlist, path: str, }; // All possible errors returned by dir functions. type dir_error = !(fs::error | json::error | errors::error | nomem); // Disables all mods in the directory except for the base mod. fn dir_disable_all(dir: *dir) void = { for (let entry &.. dir.modlist) { if (entry.name != "base") entry.enabled = false; }; fmt::println("Disabled all mods")!; }; // Locates the requested mod in a [[dir]], returning its directory entry and // modlist entry. fn dir_find( dir: *dir, mod: *modident ) ((*modfile, *modlist_entry) | !void) = { const name = mod.0; const ver = mod.1; let file: (*modfile | void) = void; for (let mod &.. dir.modfiles) { if (mod.ident.0 == name) { if (ver is version && version_cmp( ver as version, mod.ident.1 as version ) != version_cmpres::EQ) continue; file = mod; break; }; }; let entry: (*modlist_entry | void) = void; for (let mod &.. dir.modlist) { if (mod.name == name) { entry = mod; break; }; }; if (file is *modfile && entry is *modlist_entry) return (file as *modfile, entry as *modlist_entry); }; // Frees resources related to a [[dir]]. fn dir_free(dir: *dir) void = { free(dir.path); for (let modfile &.. dir.modfiles) modfile_free(modfile); modlist_free(dir.modlist); }; // Parses a mods directory. The return value must be freed with [[dir_free]]. fn dir_load(path: str) (dir | dir_error) = { // Parse mod list first in case it's not there const modlist_path = path::init(path, "mod-list.json")!; const mod_list = os::open(path::string(&modlist_path))?; defer io::close(mod_list)!; const modlist_json = json::load(mod_list)?; defer json::finish(modlist_json); const modlist = modlist_unmarshal(modlist_json); // Construct modfiles const dirents = os::readdir(path)?; let modfiles: []modfile = []; for (let entry &.. dirents) { const file = modfile_load(path, entry); // TODO: Print errors here if (file is modfile) append(modfiles, file as modfile)!; }; return dir { modfiles = modfiles, modlist = modlist, path = strings::dup(path)!, }; }; // Saves changes made to mod-list.json fn dir_save(dir: *dir) void = { let modlist_path = path::init(dir.path, "mod-list.json")!; let file = os::open( path::string(&modlist_path), fs::flag::TRUNC | fs::flag::WRONLY )!; const json = modlist_marshal(dir.modlist); json::dump(file, json)!; json::finish(json); io::close(file)!; }; // Converts a [[dir_error]] into a user-friendly string. fn dir_strerror(err: dir_error) str = { match (err) { case let err: fs::error => return fs::strerror(err); case let err: json::error => return json::strerror(err); case let err: errors::error => return errors::strerror(err); }; }; // Updates the enabled status of the given mods in mod-list.json. fn dir_update_enabled(dir: *dir, mods: []modident, enabled: bool) void = { for (let mod &.. mods) { match (dir_find(dir, mod)) { case let entry: (*modfile, *modlist_entry) => entry.1.enabled = enabled; if (enabled) { const modstr = modident_str(&entry.0.ident); fmt::println("Enabled", modstr)!; free(modstr); } else { fmt::println("Disabled", mod.0)!; }; case => const modstr = modident_str(mod); defer free(modstr); fmt::errorln(modstr, "was not found in the mods directory")!; }; }; };