summaryrefslogtreecommitdiff
path: root/src/SMAPI.Toolkit/Framework/ModData/ModDataRecord.cs
blob: 794ad2e4e176989a7af19acf25dc7911c52bbf5e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
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>The mod warnings to suppress, even if they'd normally be shown.</summary>
        public ModWarning SuppressWarnings { get; set; }

        /// <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.SuppressWarnings = model.SuppressWarnings;
            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;
        }
    }
}