using System; using System.Collections.Generic; using System.Linq; namespace StardewModdingAPI.Toolkit.Framework.ModData { /// <summary>The parsed mod metadata from SMAPI's internal mod list.</summary> public class ModDataRecord { /********* ** Accessors *********/ /// <summary>The mod's default display name.</summary> public string DisplayName { get; } /// <summary>The mod's current unique ID.</summary> public string ID { get; } /// <summary>The former mod IDs (if any).</summary> public string[] FormerIDs { get; } /// <summary>Maps local versions to a semantic version for update checks.</summary> public IDictionary<string, string> MapLocalVersions { get; } /// <summary>Maps remote versions to a semantic version for update checks.</summary> public IDictionary<string, string> MapRemoteVersions { get; } /// <summary>The versioned field data.</summary> public ModDataField[] Fields { get; } /********* ** Public methods *********/ /// <summary>Construct an instance.</summary> /// <param name="displayName">The mod's default display name.</param> /// <param name="model">The raw data model.</param> internal ModDataRecord(string displayName, ModDataModel model) { this.DisplayName = displayName; this.ID = model.ID; this.FormerIDs = model.GetFormerIDs().ToArray(); this.MapLocalVersions = new Dictionary<string, string>(model.MapLocalVersions, StringComparer.InvariantCultureIgnoreCase); this.MapRemoteVersions = new Dictionary<string, string>(model.MapRemoteVersions, StringComparer.InvariantCultureIgnoreCase); this.Fields = model.GetFields().ToArray(); } /// <summary>Get whether the mod has (or previously had) the given ID.</summary> /// <param name="id">The mod ID.</param> public bool HasID(string id) { // try main ID if (this.ID.Equals(id, StringComparison.InvariantCultureIgnoreCase)) return true; // try former IDs foreach (string formerID in this.FormerIDs) { if (formerID.Equals(id, StringComparison.InvariantCultureIgnoreCase)) return true; } return false; } /// <summary>Get a semantic local version for update checks.</summary> /// <param name="version">The remote version to normalise.</param> public ISemanticVersion GetLocalVersionForUpdateChecks(ISemanticVersion version) { return this.MapLocalVersions != null && this.MapLocalVersions.TryGetValue(version.ToString(), out string newVersion) ? new SemanticVersion(newVersion) : version; } /// <summary>Get a semantic remote version for update checks.</summary> /// <param name="version">The remote version to normalise.</param> public string GetRemoteVersionForUpdateChecks(string version) { // normalise version if possible if (SemanticVersion.TryParse(version, out ISemanticVersion parsed)) version = parsed.ToString(); // fetch remote version return this.MapRemoteVersions != null && this.MapRemoteVersions.TryGetValue(version, out string newVersion) ? newVersion : version; } /// <summary>Get the possible mod IDs.</summary> public IEnumerable<string> GetIDs() { return this.FormerIDs .Concat(new[] { this.ID }) .Where(p => !string.IsNullOrWhiteSpace(p)) .Select(p => p.Trim()) .Distinct(); } /// <summary>Get the default update key for this mod, if any.</summary> public string GetDefaultUpdateKey() { string updateKey = this.Fields.FirstOrDefault(p => p.Key == ModDataFieldKey.UpdateKey && p.IsDefault)?.Value; return !string.IsNullOrWhiteSpace(updateKey) ? updateKey : null; } /// <summary>Get a parsed representation of the <see cref="ModDataRecord.Fields"/> which match a given manifest.</summary> /// <param name="manifest">The manifest to match.</param> public ModDataRecordVersionedFields GetVersionedFields(IManifest manifest) { ModDataRecordVersionedFields parsed = new ModDataRecordVersionedFields { DisplayName = this.DisplayName, DataRecord = this }; foreach (ModDataField field in this.Fields.Where(field => field.IsMatch(manifest))) { switch (field.Key) { // update key case ModDataFieldKey.UpdateKey: parsed.UpdateKey = field.Value; break; // alternative URL case ModDataFieldKey.AlternativeUrl: parsed.AlternativeUrl = field.Value; break; // status case ModDataFieldKey.Status: parsed.Status = (ModStatus)Enum.Parse(typeof(ModStatus), field.Value, ignoreCase: true); parsed.StatusUpperVersion = field.UpperVersion; break; // status reason phrase case ModDataFieldKey.StatusReasonPhrase: parsed.StatusReasonPhrase = field.Value; break; } } return parsed; } } }