/// <summary>Parse version info for the given mod page info.</summary> /// <param name="page">The mod page info.</param> /// <param name="subkey">The optional update subkey to match in available files. (If no file names or descriptions contain the subkey, it'll be ignored.)</param> /// <param name="mapRemoteVersions">The changes to apply to remote versions for update checks.</param> /// <param name="allowNonStandardVersions">Whether to allow non-standard versions.</param> public ModInfoModel GetPageVersions(IModPage page, string subkey, bool allowNonStandardVersions, ChangeDescriptor mapRemoteVersions) { // get base model ModInfoModel model = new ModInfoModel() .SetBasicInfo(page.Name, page.Url) .SetError(page.Status, page.Error); if (page.Status != RemoteModStatus.Ok) { return(model); } // fetch versions bool hasVersions = this.TryGetLatestVersions(page, subkey, allowNonStandardVersions, mapRemoteVersions, out ISemanticVersion mainVersion, out ISemanticVersion previewVersion); if (!hasVersions && subkey != null) { hasVersions = this.TryGetLatestVersions(page, null, allowNonStandardVersions, mapRemoteVersions, out mainVersion, out previewVersion); } if (!hasVersions) { return(model.SetError(RemoteModStatus.InvalidData, $"The {page.Site} mod with ID '{page.Id}' has no valid versions.")); } // return info return(model.SetVersions(mainVersion, previewVersion)); }
/********* ** Private methods *********/ /// <summary>Get the mod version numbers for the given mod.</summary> /// <param name="mod">The mod to check.</param> /// <param name="subkey">The optional update subkey to match in available files. (If no file names or descriptions contain the subkey, it'll be ignored.)</param> /// <param name="allowNonStandardVersions">Whether to allow non-standard versions.</param> /// <param name="mapRemoteVersions">Maps remote versions to a semantic version for update checks.</param> /// <param name="main">The main mod version.</param> /// <param name="preview">The latest prerelease version, if newer than <paramref name="main"/>.</param> private bool TryGetLatestVersions(IModPage mod, string subkey, bool allowNonStandardVersions, IDictionary <string, string> mapRemoteVersions, out ISemanticVersion main, out ISemanticVersion preview) { main = null; preview = null; ISemanticVersion ParseVersion(string raw) { raw = this.NormalizeVersion(raw); return(this.GetMappedVersion(raw, mapRemoteVersions, allowNonStandardVersions)); } if (mod != null) { // get mod version if (subkey == null) { main = ParseVersion(mod.Version); } // get file versions foreach (IModDownload download in mod.Downloads) { // check for subkey if specified if (subkey != null && download.Name?.Contains(subkey, StringComparison.OrdinalIgnoreCase) != true && download.Description?.Contains(subkey, StringComparison.OrdinalIgnoreCase) != true) { continue; } // parse version ISemanticVersion cur = ParseVersion(download.Version); if (cur == null) { continue; } // track highest versions if (main == null || cur.IsNewerThan(main)) { main = cur; } if (cur.IsPrerelease() && (preview == null || cur.IsNewerThan(preview))) { preview = cur; } } if (preview != null && !preview.IsNewerThan(main)) { preview = null; } } return(main != null); }
/// <summary>Save data fetched for a mod.</summary> /// <param name="site">The mod site on which the mod is found.</param> /// <param name="id">The mod's unique ID within the <paramref name="site"/>.</param> /// <param name="mod">The mod data.</param> public void SaveMod(ModSiteKey site, string id, IModPage mod) { string key = this.GetKey(site, id); this.Mods[key] = new Cached <IModPage>(mod); }
/********* ** Private methods *********/ /// <summary>Get the mod version numbers for the given mod.</summary> /// <param name="mod">The mod to check.</param> /// <param name="subkey">The optional update subkey to match in available files. (If no file names or descriptions contain the subkey, it'll be ignored.)</param> /// <param name="allowNonStandardVersions">Whether to allow non-standard versions.</param> /// <param name="mapRemoteVersions">The changes to apply to remote versions for update checks.</param> /// <param name="main">The main mod version.</param> /// <param name="preview">The latest prerelease version, if newer than <paramref name="main"/>.</param> private bool TryGetLatestVersions(IModPage mod, string subkey, bool allowNonStandardVersions, ChangeDescriptor mapRemoteVersions, out ISemanticVersion main, out ISemanticVersion preview) { main = null; preview = null; // parse all versions from the mod page IEnumerable <(string name, string description, ISemanticVersion version)> GetAllVersions() { if (mod != null) { ISemanticVersion ParseAndMapVersion(string raw) { raw = this.NormalizeVersion(raw); return(this.GetMappedVersion(raw, mapRemoteVersions, allowNonStandardVersions)); } // get mod version ISemanticVersion modVersion = ParseAndMapVersion(mod.Version); if (modVersion != null) { yield return(name : null, description : null, version : ParseAndMapVersion(mod.Version)); } // get file versions foreach (IModDownload download in mod.Downloads) { ISemanticVersion cur = ParseAndMapVersion(download.Version); if (cur != null) { yield return(download.Name, download.Description, cur); } } } } var versions = GetAllVersions() .OrderByDescending(p => p.version, SemanticVersionComparer.Instance) .ToArray(); // get main + preview versions void TryGetVersions(out ISemanticVersion mainVersion, out ISemanticVersion previewVersion, Func <(string name, string description, ISemanticVersion version), bool> filter = null) { mainVersion = null; previewVersion = null; // get latest main + preview version foreach (var entry in versions) { if (filter?.Invoke(entry) == false) { continue; } if (entry.version.IsPrerelease()) { previewVersion ??= entry.version; } else { mainVersion ??= entry.version; } if (mainVersion != null) { break; // any other values will be older } } // normalize values if (previewVersion is not null) { mainVersion ??= previewVersion; // if every version is prerelease, latest one is the main version if (!previewVersion.IsNewerThan(mainVersion)) { previewVersion = null; } } } if (subkey is not null) { TryGetVersions(out main, out preview, entry => entry.name?.Contains(subkey, StringComparison.OrdinalIgnoreCase) == true || entry.description?.Contains(subkey, StringComparison.OrdinalIgnoreCase) == true); } if (main is null) { TryGetVersions(out main, out preview); } return(main != null); }