using System; using System.Diagnostics.CodeAnalysis; namespace StardewModdingAPI.Toolkit.Framework.UpdateData { /// A namespaced mod ID which uniquely identifies a mod within a mod repository. public class UpdateKey : IEquatable { /********* ** Accessors *********/ /// The raw update key text. public string RawText { get; } /// The mod site containing the mod. public ModSiteKey Site { get; } /// The mod ID within the repository. public string? ID { get; } /// If specified, a substring in download names/descriptions to match. public string? Subkey { get; } /// Whether the update key seems to be valid. #if NET5_0_OR_GREATER [MemberNotNullWhen(true, nameof(UpdateKey.ID))] #endif public bool LooksValid { get; } /********* ** Public methods *********/ /// Construct an instance. /// The raw update key text. /// The mod site containing the mod. /// The mod ID within the site. /// If specified, a substring in download names/descriptions to match. public UpdateKey(string? rawText, ModSiteKey site, string? id, string? subkey) { this.RawText = rawText?.Trim() ?? string.Empty; this.Site = site; this.ID = id?.Trim(); this.Subkey = subkey?.Trim(); this.LooksValid = site != ModSiteKey.Unknown && !string.IsNullOrWhiteSpace(id); } /// Construct an instance. /// The mod site containing the mod. /// The mod ID within the site. /// If specified, a substring in download names/descriptions to match. public UpdateKey(ModSiteKey site, string? id, string? subkey) : this(UpdateKey.GetString(site, id, subkey), site, id, subkey) { } /// Parse a raw update key. /// The raw update key to parse. public static UpdateKey Parse(string? raw) { // extract site + ID string? rawSite; string? id; { string[]? parts = raw?.Trim().Split(':'); if (parts?.Length != 2) return new UpdateKey(raw, ModSiteKey.Unknown, null, null); rawSite = parts[0].Trim(); id = parts[1].Trim(); } if (string.IsNullOrWhiteSpace(id)) id = null; // extract subkey string? subkey = null; if (id != null) { string[] parts = id.Split('@'); if (parts.Length == 2) { id = parts[0].Trim(); subkey = $"@{parts[1]}".Trim(); } } // parse if (!Enum.TryParse(rawSite, true, out ModSiteKey site)) return new UpdateKey(raw, ModSiteKey.Unknown, id, subkey); if (id == null) return new UpdateKey(raw, site, null, subkey); return new UpdateKey(raw, site, id, subkey); } /// Parse a raw update key if it's valid. /// The raw update key to parse. /// The parsed update key, if valid. /// Returns whether the update key was successfully parsed. public static bool TryParse(string raw, out UpdateKey parsed) { parsed = UpdateKey.Parse(raw); return parsed.LooksValid; } /// Get a string that represents the current object. public override string ToString() { return this.LooksValid ? UpdateKey.GetString(this.Site, this.ID, this.Subkey) : this.RawText; } /// Indicates whether the current object is equal to another object of the same type. /// An object to compare with this object. public bool Equals(UpdateKey? other) { if (!this.LooksValid) { return other?.LooksValid == false && this.RawText.Equals(other.RawText, StringComparison.OrdinalIgnoreCase); } return other != null && this.Site == other.Site && string.Equals(this.ID, other.ID, StringComparison.OrdinalIgnoreCase) && string.Equals(this.Subkey, other.Subkey, StringComparison.OrdinalIgnoreCase); } /// Determines whether the specified object is equal to the current object. /// The object to compare with the current object. public override bool Equals(object? obj) { return obj is UpdateKey other && this.Equals(other); } /// Serves as the default hash function. /// A hash code for the current object. public override int GetHashCode() { return this.ToString().ToLower().GetHashCode(); } /// Get the string representation of an update key. /// The mod site containing the mod. /// The mod ID within the repository. /// If specified, a substring in download names/descriptions to match. public static string GetString(ModSiteKey site, string? id, string? subkey = null) { return $"{site}:{id}{subkey}".Trim(); } } }