From 5f19e4f2035c36f9c6c882da3767d6f29409db1c Mon Sep 17 00:00:00 2001
From: Jesse Plamondon-Willard <github@jplamondonw.com>
Date: Wed, 27 Jun 2018 00:05:53 -0400
Subject: move mod DB parsing into toolkit (#532)

---
 src/SMAPI/Framework/IModMetadata.cs                |   2 +-
 src/SMAPI/Framework/ModData/ModDataField.cs        |  82 ------------
 src/SMAPI/Framework/ModData/ModDataFieldKey.cs     |  18 ---
 src/SMAPI/Framework/ModData/ModDataRecord.cs       | 146 ---------------------
 src/SMAPI/Framework/ModData/ModDatabase.cs         | 140 --------------------
 src/SMAPI/Framework/ModData/ModStatus.cs           |  18 ---
 src/SMAPI/Framework/ModData/ParsedModDataRecord.cs |  51 -------
 src/SMAPI/Framework/ModLoading/ModMetadata.cs      |   2 +-
 src/SMAPI/Framework/ModLoading/ModResolver.cs      |   2 +-
 src/SMAPI/Framework/Models/SMetadata.cs            |  15 ---
 10 files changed, 3 insertions(+), 473 deletions(-)
 delete mode 100644 src/SMAPI/Framework/ModData/ModDataField.cs
 delete mode 100644 src/SMAPI/Framework/ModData/ModDataFieldKey.cs
 delete mode 100644 src/SMAPI/Framework/ModData/ModDataRecord.cs
 delete mode 100644 src/SMAPI/Framework/ModData/ModDatabase.cs
 delete mode 100644 src/SMAPI/Framework/ModData/ModStatus.cs
 delete mode 100644 src/SMAPI/Framework/ModData/ParsedModDataRecord.cs
 delete mode 100644 src/SMAPI/Framework/Models/SMetadata.cs

(limited to 'src/SMAPI/Framework')

diff --git a/src/SMAPI/Framework/IModMetadata.cs b/src/SMAPI/Framework/IModMetadata.cs
index 6281c052..5a8689de 100644
--- a/src/SMAPI/Framework/IModMetadata.cs
+++ b/src/SMAPI/Framework/IModMetadata.cs
@@ -1,6 +1,6 @@
-using StardewModdingAPI.Framework.ModData;
 using StardewModdingAPI.Framework.ModLoading;
 using StardewModdingAPI.Toolkit.Framework.Clients.WebApi;
+using StardewModdingAPI.Toolkit.Framework.ModData;
 
 namespace StardewModdingAPI.Framework
 {
diff --git a/src/SMAPI/Framework/ModData/ModDataField.cs b/src/SMAPI/Framework/ModData/ModDataField.cs
deleted file mode 100644
index df906103..00000000
--- a/src/SMAPI/Framework/ModData/ModDataField.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-using System.Linq;
-
-namespace StardewModdingAPI.Framework.ModData
-{
-    /// <summary>A versioned mod metadata field.</summary>
-    internal class ModDataField
-    {
-        /*********
-        ** Accessors
-        *********/
-        /// <summary>The field key.</summary>
-        public ModDataFieldKey Key { get; }
-
-        /// <summary>The field value.</summary>
-        public string Value { get; }
-
-        /// <summary>Whether this field should only be applied if it's not already set.</summary>
-        public bool IsDefault { get; }
-
-        /// <summary>The lowest version in the range, or <c>null</c> for all past versions.</summary>
-        public ISemanticVersion LowerVersion { get; }
-
-        /// <summary>The highest version in the range, or <c>null</c> for all future versions.</summary>
-        public ISemanticVersion UpperVersion { get; }
-
-
-        /*********
-        ** Public methods
-        *********/
-        /// <summary>Construct an instance.</summary>
-        /// <param name="key">The field key.</param>
-        /// <param name="value">The field value.</param>
-        /// <param name="isDefault">Whether this field should only be applied if it's not already set.</param>
-        /// <param name="lowerVersion">The lowest version in the range, or <c>null</c> for all past versions.</param>
-        /// <param name="upperVersion">The highest version in the range, or <c>null</c> for all future versions.</param>
-        public ModDataField(ModDataFieldKey key, string value, bool isDefault, ISemanticVersion lowerVersion, ISemanticVersion upperVersion)
-        {
-            this.Key = key;
-            this.Value = value;
-            this.IsDefault = isDefault;
-            this.LowerVersion = lowerVersion;
-            this.UpperVersion = upperVersion;
-        }
-
-        /// <summary>Get whether this data field applies for the given manifest.</summary>
-        /// <param name="manifest">The mod manifest.</param>
-        public bool IsMatch(IManifest manifest)
-        {
-            return
-                manifest?.Version != null // ignore invalid manifest
-                && (!this.IsDefault || !this.HasFieldValue(manifest, this.Key))
-                && (this.LowerVersion == null || !manifest.Version.IsOlderThan(this.LowerVersion))
-                && (this.UpperVersion == null || !manifest.Version.IsNewerThan(this.UpperVersion));
-        }
-
-
-        /*********
-        ** Private methods
-        *********/
-        /// <summary>Get whether a manifest field has a meaningful value for the purposes of enforcing <see cref="IsDefault"/>.</summary>
-        /// <param name="manifest">The mod manifest.</param>
-        /// <param name="key">The field key matching <see cref="ModDataFieldKey"/>.</param>
-        private bool HasFieldValue(IManifest manifest, ModDataFieldKey key)
-        {
-            switch (key)
-            {
-                // update key
-                case ModDataFieldKey.UpdateKey:
-                    return manifest.UpdateKeys != null && manifest.UpdateKeys.Any(p => !string.IsNullOrWhiteSpace(p));
-
-                // non-manifest fields
-                case ModDataFieldKey.AlternativeUrl:
-                case ModDataFieldKey.StatusReasonPhrase:
-                case ModDataFieldKey.Status:
-                    return false;
-
-                default:
-                    return false;
-            }
-        }
-    }
-}
diff --git a/src/SMAPI/Framework/ModData/ModDataFieldKey.cs b/src/SMAPI/Framework/ModData/ModDataFieldKey.cs
deleted file mode 100644
index f68f575c..00000000
--- a/src/SMAPI/Framework/ModData/ModDataFieldKey.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace StardewModdingAPI.Framework.ModData
-{
-    /// <summary>The valid field keys.</summary>
-    public enum ModDataFieldKey
-    {
-        /// <summary>A manifest update key.</summary>
-        UpdateKey,
-
-        /// <summary>An alternative URL the player can check for an updated version.</summary>
-        AlternativeUrl,
-
-        /// <summary>The mod's predefined compatibility status.</summary>
-        Status,
-
-        /// <summary>A reason phrase for the <see cref="Status"/>, or <c>null</c> to use the default reason.</summary>
-        StatusReasonPhrase
-    }
-}
diff --git a/src/SMAPI/Framework/ModData/ModDataRecord.cs b/src/SMAPI/Framework/ModData/ModDataRecord.cs
deleted file mode 100644
index 56275f53..00000000
--- a/src/SMAPI/Framework/ModData/ModDataRecord.cs
+++ /dev/null
@@ -1,146 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.Serialization;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-
-namespace StardewModdingAPI.Framework.ModData
-{
-    /// <summary>Raw mod metadata from SMAPI's internal mod list.</summary>
-    internal class ModDataRecord
-    {
-        /*********
-        ** Properties
-        *********/
-        /// <summary>This field stores properties that aren't mapped to another field before they're parsed into <see cref="Fields"/>.</summary>
-        [JsonExtensionData]
-        private IDictionary<string, JToken> ExtensionData;
-
-
-        /*********
-        ** Accessors
-        *********/
-        /// <summary>The mod's current unique ID.</summary>
-        public string ID { get; set; }
-
-        /// <summary>The former mod IDs (if any).</summary>
-        /// <remarks>
-        /// This uses a custom format which uniquely identifies a mod across multiple versions and
-        /// supports matching other fields if no ID was specified. This doesn't include the latest
-        /// ID, if any. Format rules:
-        ///   1. If the mod's ID changed over time, multiple variants can be separated by the
-        ///      <c>|</c> character.
-        ///   2. Each variant can take one of two forms:
-        ///      - A simple string matching the mod's UniqueID value.
-        ///      - A JSON structure containing any of four manifest fields (ID, Name, Author, and
-        ///        EntryDll) to match.
-        /// </remarks>
-        public string FormerIDs { get; set; }
-
-        /// <summary>Maps local versions to a semantic version for update checks.</summary>
-        public IDictionary<string, string> MapLocalVersions { get; set; } = new Dictionary<string, string>();
-
-        /// <summary>Maps remote versions to a semantic version for update checks.</summary>
-        public IDictionary<string, string> MapRemoteVersions { get; set; } = new Dictionary<string, string>();
-
-        /// <summary>The versioned field data.</summary>
-        /// <remarks>
-        /// This maps field names to values. This should be accessed via <see cref="GetFields"/>.
-        /// Format notes:
-        ///   - Each key consists of a field name prefixed with any combination of version range
-        ///     and <c>Default</c>, separated by pipes (whitespace trimmed). For example, <c>Name</c>
-        ///     will always override the name, <c>Default | Name</c> will only override a blank
-        ///     name, and <c>~1.1 | Default | Name</c> will override blank names up to version 1.1.
-        ///   - The version format is <c>min~max</c> (where either side can be blank for unbounded), or
-        ///     a single version number.
-        ///   - The field name itself corresponds to a <see cref="ModDataFieldKey"/> value.
-        /// </remarks>
-        public IDictionary<string, string> Fields { get; set; } = new Dictionary<string, string>();
-
-
-        /*********
-        ** Public methods
-        *********/
-        /// <summary>Get a parsed representation of the <see cref="Fields"/>.</summary>
-        public IEnumerable<ModDataField> GetFields()
-        {
-            foreach (KeyValuePair<string, string> pair in this.Fields)
-            {
-                // init fields
-                string packedKey = pair.Key;
-                string value = pair.Value;
-                bool isDefault = false;
-                ISemanticVersion lowerVersion = null;
-                ISemanticVersion upperVersion = null;
-
-                // parse
-                string[] parts = packedKey.Split('|').Select(p => p.Trim()).ToArray();
-                ModDataFieldKey fieldKey = (ModDataFieldKey)Enum.Parse(typeof(ModDataFieldKey), parts.Last(), ignoreCase: true);
-                foreach (string part in parts.Take(parts.Length - 1))
-                {
-                    // 'default'
-                    if (part.Equals("Default", StringComparison.InvariantCultureIgnoreCase))
-                    {
-                        isDefault = true;
-                        continue;
-                    }
-
-                    // version range
-                    if (part.Contains("~"))
-                    {
-                        string[] versionParts = part.Split(new[] { '~' }, 2);
-                        lowerVersion = versionParts[0] != "" ? new SemanticVersion(versionParts[0]) : null;
-                        upperVersion = versionParts[1] != "" ? new SemanticVersion(versionParts[1]) : null;
-                        continue;
-                    }
-
-                    // single version
-                    lowerVersion = new SemanticVersion(part);
-                    upperVersion = new SemanticVersion(part);
-                }
-
-                yield return new ModDataField(fieldKey, value, isDefault, lowerVersion, upperVersion);
-            }
-        }
-
-        /// <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;
-        }
-
-
-        /*********
-        ** Private methods
-        *********/
-        /// <summary>The method invoked after JSON deserialisation.</summary>
-        /// <param name="context">The deserialisation context.</param>
-        [OnDeserialized]
-        private void OnDeserialized(StreamingContext context)
-        {
-            if (this.ExtensionData != null)
-            {
-                this.Fields = this.ExtensionData.ToDictionary(p => p.Key, p => p.Value.ToString());
-                this.ExtensionData = null;
-            }
-        }
-    }
-}
diff --git a/src/SMAPI/Framework/ModData/ModDatabase.cs b/src/SMAPI/Framework/ModData/ModDatabase.cs
deleted file mode 100644
index 62f37d9b..00000000
--- a/src/SMAPI/Framework/ModData/ModDatabase.cs
+++ /dev/null
@@ -1,140 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace StardewModdingAPI.Framework.ModData
-{
-    /// <summary>Handles access to SMAPI's internal mod metadata list.</summary>
-    internal class ModDatabase
-    {
-        /*********
-        ** Properties
-        *********/
-        /// <summary>The underlying mod data records indexed by default display name.</summary>
-        private readonly IDictionary<string, ModDataRecord> Records;
-
-        /// <summary>Get an update URL for an update key (if valid).</summary>
-        private readonly Func<string, string> GetUpdateUrl;
-
-
-        /*********
-        ** Public methods
-        *********/
-        /// <summary>Construct an empty instance.</summary>
-        public ModDatabase()
-        : this(new Dictionary<string, ModDataRecord>(), key => null) { }
-
-        /// <summary>Construct an instance.</summary>
-        /// <param name="records">The underlying mod data records indexed by default display name.</param>
-        /// <param name="getUpdateUrl">Get an update URL for an update key (if valid).</param>
-        public ModDatabase(IDictionary<string, ModDataRecord> records, Func<string, string> getUpdateUrl)
-        {
-            this.Records = records;
-            this.GetUpdateUrl = getUpdateUrl;
-        }
-
-        /// <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 ParsedModDataRecord GetParsed(IManifest manifest)
-        {
-            // get raw record
-            if (!this.TryGetRaw(manifest?.UniqueID, out string displayName, out ModDataRecord record))
-                return null;
-
-            // parse fields
-            ParsedModDataRecord parsed = new ParsedModDataRecord { DisplayName = displayName, DataRecord = record };
-            foreach (ModDataField field in record.GetFields().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;
-        }
-
-        /// <summary>Get the display name for a given mod ID (if available).</summary>
-        /// <param name="id">The unique mod ID.</param>
-        public string GetDisplayNameFor(string id)
-        {
-            return this.TryGetRaw(id, out string displayName, out ModDataRecord _)
-                ? displayName
-                : null;
-        }
-
-        /// <summary>Get the mod page URL for a mod (if available).</summary>
-        /// <param name="id">The unique mod ID.</param>
-        public string GetModPageUrlFor(string id)
-        {
-            // get raw record
-            if (!this.TryGetRaw(id, out string _, out ModDataRecord record))
-                return null;
-
-            // get update key
-            ModDataField updateKeyField = record.GetFields().FirstOrDefault(p => p.Key == ModDataFieldKey.UpdateKey);
-            if (updateKeyField == null)
-                return null;
-
-            // get update URL
-            return this.GetUpdateUrl(updateKeyField.Value);
-        }
-
-
-        /*********
-        ** Private models
-        *********/
-        /// <summary>Get a raw data record.</summary>
-        /// <param name="id">The mod ID to match.</param>
-        /// <param name="displayName">The mod's default display name.</param>
-        /// <param name="record">The raw mod record.</param>
-        private bool TryGetRaw(string id, out string displayName, out ModDataRecord record)
-        {
-            if (!string.IsNullOrWhiteSpace(id))
-            {
-                foreach (var entry in this.Records)
-                {
-                    displayName = entry.Key;
-                    record = entry.Value;
-
-                    // try main ID
-                    if (record.ID != null && record.ID.Equals(id, StringComparison.InvariantCultureIgnoreCase))
-                        return true;
-
-                    // try former IDs
-                    if (record.FormerIDs != null)
-                    {
-                        foreach (string part in record.FormerIDs.Split('|'))
-                        {
-                            if (part.Trim().Equals(id, StringComparison.InvariantCultureIgnoreCase))
-                                return true;
-                        }
-                    }
-                }
-            }
-
-            displayName = null;
-            record = null;
-            return false;
-        }
-    }
-}
diff --git a/src/SMAPI/Framework/ModData/ModStatus.cs b/src/SMAPI/Framework/ModData/ModStatus.cs
deleted file mode 100644
index 0e1d94d4..00000000
--- a/src/SMAPI/Framework/ModData/ModStatus.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace StardewModdingAPI.Framework.ModData
-{
-    /// <summary>Indicates how SMAPI should treat a mod.</summary>
-    internal enum ModStatus
-    {
-        /// <summary>Don't override the status.</summary>
-        None,
-
-        /// <summary>The mod is obsolete and shouldn't be used, regardless of version.</summary>
-        Obsolete,
-
-        /// <summary>Assume the mod is not compatible, even if SMAPI doesn't detect any incompatible code.</summary>
-        AssumeBroken,
-
-        /// <summary>Assume the mod is compatible, even if SMAPI detects incompatible code.</summary>
-        AssumeCompatible
-    }
-}
diff --git a/src/SMAPI/Framework/ModData/ParsedModDataRecord.cs b/src/SMAPI/Framework/ModData/ParsedModDataRecord.cs
deleted file mode 100644
index 3801fac3..00000000
--- a/src/SMAPI/Framework/ModData/ParsedModDataRecord.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-namespace StardewModdingAPI.Framework.ModData
-{
-    /// <summary>A parsed representation of the fields from a <see cref="ModDataRecord"/> for a specific manifest.</summary>
-    internal class ParsedModDataRecord
-    {
-        /*********
-        ** Accessors
-        *********/
-        /// <summary>The underlying data record.</summary>
-        public ModDataRecord DataRecord { get; set; }
-
-        /// <summary>The default mod name to display when the name isn't available (e.g. during dependency checks).</summary>
-        public string DisplayName { get; set; }
-
-        /// <summary>The update key to apply.</summary>
-        public string UpdateKey { get; set; }
-
-        /// <summary>The alternative URL the player can check for an updated version.</summary>
-        public string AlternativeUrl { get; set; }
-
-        /// <summary>The predefined compatibility status.</summary>
-        public ModStatus Status { get; set; } = ModStatus.None;
-
-        /// <summary>A reason phrase for the <see cref="Status"/>, or <c>null</c> to use the default reason.</summary>
-        public string StatusReasonPhrase { get; set; }
-
-        /// <summary>The upper version for which the <see cref="Status"/> applies (if any).</summary>
-        public ISemanticVersion StatusUpperVersion { get; set; }
-
-
-        /*********
-        ** Public methods
-        *********/
-        /// <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.DataRecord.GetLocalVersionForUpdateChecks(version);
-        }
-
-        /// <summary>Get a semantic remote version for update checks.</summary>
-        /// <param name="version">The remote version to normalise.</param>
-        public ISemanticVersion GetRemoteVersionForUpdateChecks(string version)
-        {
-            string rawVersion = this.DataRecord.GetRemoteVersionForUpdateChecks(version);
-            return rawVersion != null
-                ? new SemanticVersion(rawVersion)
-                : null;
-        }
-    }
-}
diff --git a/src/SMAPI/Framework/ModLoading/ModMetadata.cs b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
index 3a412009..4db25932 100644
--- a/src/SMAPI/Framework/ModLoading/ModMetadata.cs
+++ b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
@@ -1,7 +1,7 @@
 using System;
 using System.Linq;
-using StardewModdingAPI.Framework.ModData;
 using StardewModdingAPI.Toolkit.Framework.Clients.WebApi;
+using StardewModdingAPI.Toolkit.Framework.ModData;
 
 namespace StardewModdingAPI.Framework.ModLoading
 {
diff --git a/src/SMAPI/Framework/ModLoading/ModResolver.cs b/src/SMAPI/Framework/ModLoading/ModResolver.cs
index fde921e6..c1bc51ec 100644
--- a/src/SMAPI/Framework/ModLoading/ModResolver.cs
+++ b/src/SMAPI/Framework/ModLoading/ModResolver.cs
@@ -3,7 +3,7 @@ using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Text.RegularExpressions;
-using StardewModdingAPI.Framework.ModData;
+using StardewModdingAPI.Toolkit.Framework.ModData;
 using StardewModdingAPI.Toolkit.Serialisation;
 using StardewModdingAPI.Toolkit.Serialisation.Models;
 using StardewModdingAPI.Toolkit.Utilities;
diff --git a/src/SMAPI/Framework/Models/SMetadata.cs b/src/SMAPI/Framework/Models/SMetadata.cs
deleted file mode 100644
index 9ff495e9..00000000
--- a/src/SMAPI/Framework/Models/SMetadata.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System.Collections.Generic;
-using StardewModdingAPI.Framework.ModData;
-
-namespace StardewModdingAPI.Framework.Models
-{
-    /// <summary>The SMAPI predefined metadata.</summary>
-    internal class SMetadata
-    {
-        /********
-        ** Accessors
-        ********/
-        /// <summary>Extra metadata about mods.</summary>
-        public IDictionary<string, ModDataRecord> ModData { get; set; }
-    }
-}
-- 
cgit