use fmt; use strconv; use strings; // A [[version]] has four components, being major, minor, patch, and build. // Build is only used for internal game mechanics, and is not usable by mods. type version = [4]u16; // Any error which may be returned by a version function. type version_error = !(strconv::error | version_invalid_len); // Indicates that the version string has too many or too few parts. Contains // the number of parts received. type version_invalid_len = !size; // Indicates the result of comparing two [[version]]s. Also contains some extra // variants for dependency validation. // // The value is encoded in binary as '(eq)(lt)(gt)'. type version_cmpres = enum u8 { ANY = 0, EQ, GT, GTEQ, LT, LTEQ = 6, }; // Compares two [[version]]s for equality. fn version_cmp(v1: version, v2: version) version_cmpres = { for (let i = 0z; i < len(v1); i += 1) { if (v1[i] > v2[i]) { return version_cmpres::GT; } else if (v1[i] < v2[i]) { return version_cmpres::LT; }; }; return version_cmpres::EQ; }; // Parses a [[version]] from an input string. fn version_parse(input: str) (version | version_error) = { let parts = strings::split(strings::trim(input), ".")!; if (len(parts) < 2 || len(parts) > 4) return len(parts): version_invalid_len; let version: version = [0...]; for (let i = 0z; i < len(parts); i += 1) version[i] = strconv::stou16(parts[i])?; return version; }; // Formats a [[version]] for printing. The return value must be freed by the // caller. fn version_str(ver: version, include_build: bool) str = { if (include_build) { return fmt::asprintf("{}.{}.{}.{}", ver[0], ver[1], ver[2], ver[3])!; } else { return fmt::asprintf("{}.{}.{}", ver[0], ver[1], ver[2])!; }; }; // Converts a [[version_error]] into a user-friendly string. fn version_strerror(err: version_error) str = { match (err) { case version_invalid_len => return "Invalid number of version parts"; case let err: strconv::error => return strconv::strerror(err); }; };