use fmt; use strings; // A representation of a mod, with a name and optional version. type modident = (str, (version | void)); // Frees resources related to a [[modident]]. fn modident_free(mod: *modident) void = { free(mod.0); }; // Frees resources related to a slice of [[modident]]. fn modident_free_all(list: []modident) void = { for (let ident &.. list) { modident_free(ident); }; free(list); }; // Parses an input string into a [[modident]]. The input string will not be // interpreted as a mod set. The return value must be freed with // [[modident_free]]. fn modident_parse(input: str) modident = { const input = strings::trimsuffix(input, ".zip"); const parts = strings::split(input, "_")!; defer free(parts); if (len(parts) == 1) return (strings::dup(input)!, void); const name = strings::concat(parts[..len(parts) - 1]...)!; match (version_parse(parts[len(parts) - 1])) { case let ver: version => return (name, ver); case => return (name, void); }; }; // Parses a slice of input strings into a slice of [[modident]]. If the input is // a mod set name, it will be expanded. The return value must be freed with // [[modident_free_all]]. fn modident_parseall(input: *[]str, config: *config) []modident = { let mods: []modident = []; for (let input &.. input) { // Mod set if (strings::hasprefix(*input, '!')) { const set_name = strings::sub(*input, 1, strings::end); match (config_find_set(config, set_name)) { case let set: *modset => append(mods, set.1...)!; case => fmt::errorfln( "Mod set '{}' does not exist", set_name )!; }; continue; }; // Standard mod append(mods, modident_parse(*input))!; }; return mods; }; // Converts a [[modident]] into a user-friendly string. The return value must // be freed by the caller. fn modident_str(mod: *modident) str = { match (mod.1) { case let ver: version => const verstr = version_str(ver, false); defer free(verstr); return fmt::asprint(mod.0, verstr)!; case => return fmt::asprint(mod.0)!; }; };