summaryrefslogtreecommitdiff
path: root/src/SMAPI.Toolkit/Framework/Clients
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI.Toolkit/Framework/Clients')
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs21
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryVersionModel.cs7
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs37
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchEntryModel.cs29
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchModel.cs13
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs7
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/Wiki/ChangeDescriptor.cs23
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs161
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityInfo.cs31
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiDataOverrideEntry.cs4
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModEntry.cs96
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModList.cs21
12 files changed, 292 insertions, 158 deletions
diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs
index 2f58a3f1..4fc4ea54 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs
@@ -1,3 +1,5 @@
+using System;
+
namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
{
/// <summary>Metadata about a mod.</summary>
@@ -7,15 +9,26 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
** Accessors
*********/
/// <summary>The mod's unique ID (if known).</summary>
- public string ID { get; set; }
+ public string ID { get; }
/// <summary>The update version recommended by the web API based on its version update and mapping rules.</summary>
- public ModEntryVersionModel SuggestedUpdate { get; set; }
+ public ModEntryVersionModel? SuggestedUpdate { get; set; }
/// <summary>Optional extended data which isn't needed for update checks.</summary>
- public ModExtendedMetadataModel Metadata { get; set; }
+ public ModExtendedMetadataModel? Metadata { get; set; }
/// <summary>The errors that occurred while fetching update data.</summary>
- public string[] Errors { get; set; } = new string[0];
+ public string[] Errors { get; set; } = Array.Empty<string>();
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="id">The mod's unique ID (if known).</param>
+ public ModEntryModel(string id)
+ {
+ this.ID = id;
+ }
}
}
diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryVersionModel.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryVersionModel.cs
index 188db31d..a1e78986 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryVersionModel.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryVersionModel.cs
@@ -11,19 +11,16 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
*********/
/// <summary>The version number.</summary>
[JsonConverter(typeof(NonStandardSemanticVersionConverter))]
- public ISemanticVersion Version { get; set; }
+ public ISemanticVersion Version { get; }
/// <summary>The mod page URL.</summary>
- public string Url { get; set; }
+ public string Url { get; }
/*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
- public ModEntryVersionModel() { }
-
- /// <summary>Construct an instance.</summary>
/// <param name="version">The version number.</param>
/// <param name="url">The mod page URL.</param>
public ModEntryVersionModel(ISemanticVersion version, string url)
diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs
index 5c2ce366..272a2063 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
@@ -17,10 +18,10 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
** Mod info
****/
/// <summary>The mod's unique ID. A mod may have multiple current IDs in rare cases (e.g. due to parallel releases or unofficial updates).</summary>
- public string[] ID { get; set; } = new string[0];
+ public string[] ID { get; set; } = Array.Empty<string>();
/// <summary>The mod's display name.</summary>
- public string Name { get; set; }
+ public string? Name { get; set; }
/// <summary>The mod ID on Nexus.</summary>
public int? NexusID { get; set; }
@@ -32,31 +33,31 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
public int? CurseForgeID { get; set; }
/// <summary>The mod key in the CurseForge mod repo (used in mod page URLs).</summary>
- public string CurseForgeKey { get; set; }
+ public string? CurseForgeKey { get; set; }
/// <summary>The mod ID in the ModDrop mod repo.</summary>
public int? ModDropID { get; set; }
/// <summary>The GitHub repository in the form 'owner/repo'.</summary>
- public string GitHubRepo { get; set; }
+ public string? GitHubRepo { get; set; }
/// <summary>The URL to a non-GitHub source repo.</summary>
- public string CustomSourceUrl { get; set; }
+ public string? CustomSourceUrl { get; set; }
/// <summary>The custom mod page URL (if applicable).</summary>
- public string CustomUrl { get; set; }
+ public string? CustomUrl { get; set; }
/// <summary>The main version.</summary>
- public ModEntryVersionModel Main { get; set; }
+ public ModEntryVersionModel? Main { get; set; }
/// <summary>The latest optional version, if newer than <see cref="Main"/>.</summary>
- public ModEntryVersionModel Optional { get; set; }
+ public ModEntryVersionModel? Optional { get; set; }
/// <summary>The latest unofficial version, if newer than <see cref="Main"/> and <see cref="Optional"/>.</summary>
- public ModEntryVersionModel Unofficial { get; set; }
+ public ModEntryVersionModel? Unofficial { get; set; }
/// <summary>The latest unofficial version for the current Stardew Valley or SMAPI beta, if any.</summary>
- public ModEntryVersionModel UnofficialForBeta { get; set; }
+ public ModEntryVersionModel? UnofficialForBeta { get; set; }
/****
** Stable compatibility
@@ -66,10 +67,10 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
public WikiCompatibilityStatus? CompatibilityStatus { get; set; }
/// <summary>The human-readable summary of the compatibility status or workaround, without HTML formatting.</summary>
- public string CompatibilitySummary { get; set; }
+ public string? CompatibilitySummary { get; set; }
/// <summary>The game or SMAPI version which broke this mod, if applicable.</summary>
- public string BrokeIn { get; set; }
+ public string? BrokeIn { get; set; }
/****
** Beta compatibility
@@ -79,22 +80,22 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
public WikiCompatibilityStatus? BetaCompatibilityStatus { get; set; }
/// <summary>The human-readable summary of the compatibility status or workaround for the Stardew Valley beta (if any), without HTML formatting.</summary>
- public string BetaCompatibilitySummary { get; set; }
+ public string? BetaCompatibilitySummary { get; set; }
/// <summary>The beta game or SMAPI version which broke this mod, if applicable.</summary>
- public string BetaBrokeIn { get; set; }
+ public string? BetaBrokeIn { get; set; }
/****
** Version mappings
****/
/// <summary>A serialized change descriptor to apply to the local version during update checks (see <see cref="ChangeDescriptor"/>).</summary>
- public string ChangeLocalVersions { get; set; }
+ public string? ChangeLocalVersions { get; set; }
/// <summary>A serialized change descriptor to apply to the remote version during update checks (see <see cref="ChangeDescriptor"/>).</summary>
- public string ChangeRemoteVersions { get; set; }
+ public string? ChangeRemoteVersions { get; set; }
/// <summary>A serialized change descriptor to apply to the update keys during update checks (see <see cref="ChangeDescriptor"/>).</summary>
- public string ChangeUpdateKeys { get; set; }
+ public string? ChangeUpdateKeys { get; set; }
/*********
@@ -110,7 +111,7 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
/// <param name="optional">The latest optional version, if newer than <paramref name="main"/>.</param>
/// <param name="unofficial">The latest unofficial version, if newer than <paramref name="main"/> and <paramref name="optional"/>.</param>
/// <param name="unofficialForBeta">The latest unofficial version for the current Stardew Valley or SMAPI beta, if any.</param>
- public ModExtendedMetadataModel(WikiModEntry wiki, ModDataRecord db, ModEntryVersionModel main, ModEntryVersionModel optional, ModEntryVersionModel unofficial, ModEntryVersionModel unofficialForBeta)
+ public ModExtendedMetadataModel(WikiModEntry? wiki, ModDataRecord? db, ModEntryVersionModel? main, ModEntryVersionModel? optional, ModEntryVersionModel? unofficial, ModEntryVersionModel? unofficialForBeta)
{
// versions
this.Main = main;
diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchEntryModel.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchEntryModel.cs
index bf81e102..9c11e1db 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchEntryModel.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchEntryModel.cs
@@ -1,3 +1,6 @@
+using System;
+using System.Linq;
+
namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
{
/// <summary>Specifies the identifiers for a mod to match.</summary>
@@ -7,37 +10,39 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
** Accessors
*********/
/// <summary>The unique mod ID.</summary>
- public string ID { get; set; }
+ public string ID { get; }
/// <summary>The namespaced mod update keys (if available).</summary>
- public string[] UpdateKeys { get; set; }
+ public string[] UpdateKeys { get; private set; }
/// <summary>The mod version installed by the local player. This is used for version mapping in some cases.</summary>
- public ISemanticVersion InstalledVersion { get; set; }
+ public ISemanticVersion? InstalledVersion { get; }
/// <summary>Whether the installed version is broken or could not be loaded.</summary>
- public bool IsBroken { get; set; }
+ public bool IsBroken { get; }
/*********
** Public methods
*********/
- /// <summary>Construct an empty instance.</summary>
- public ModSearchEntryModel()
- {
- // needed for JSON deserializing
- }
-
/// <summary>Construct an instance.</summary>
/// <param name="id">The unique mod ID.</param>
/// <param name="installedVersion">The version installed by the local player. This is used for version mapping in some cases.</param>
/// <param name="updateKeys">The namespaced mod update keys (if available).</param>
/// <param name="isBroken">Whether the installed version is broken or could not be loaded.</param>
- public ModSearchEntryModel(string id, ISemanticVersion installedVersion, string[] updateKeys, bool isBroken = false)
+ public ModSearchEntryModel(string id, ISemanticVersion? installedVersion, string[]? updateKeys, bool isBroken = false)
{
this.ID = id;
this.InstalledVersion = installedVersion;
- this.UpdateKeys = updateKeys ?? new string[0];
+ this.UpdateKeys = updateKeys ?? Array.Empty<string>();
+ this.IsBroken = isBroken;
+ }
+
+ /// <summary>Add update keys for the mod.</summary>
+ /// <param name="updateKeys">The update keys to add.</param>
+ public void AddUpdateKeys(params string[] updateKeys)
+ {
+ this.UpdateKeys = this.UpdateKeys.Concat(updateKeys).ToArray();
}
}
}
diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchModel.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchModel.cs
index 73698173..3c74bab0 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchModel.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchModel.cs
@@ -1,3 +1,5 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using StardewModdingAPI.Toolkit.Utilities;
@@ -22,16 +24,23 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
public ISemanticVersion GameVersion { get; set; }
/// <summary>The OS on which the player plays.</summary>
- public Platform? Platform { get; set; }
+ public Platform Platform { get; set; }
/*********
** Public methods
*********/
/// <summary>Construct an empty instance.</summary>
+ [Obsolete("This constructor only exists to support ASP.NET model binding, and shouldn't be used directly.")]
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Used by ASP.NET model binding.")]
public ModSearchModel()
{
- // needed for JSON deserializing
+ // ASP.NET Web API needs a public empty constructor for top-level request models, and
+ // it'll fail if the other constructor is marked with [JsonConstructor]. Apparently
+ // it's fine with non-empty constructors in nested models like ModSearchEntryModel.
+ this.Mods = Array.Empty<ModSearchEntryModel>();
+ this.ApiVersion = null!;
+ this.GameVersion = null!;
}
/// <summary>Construct an instance.</summary>
diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs
index c2d906a0..d4282617 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs
@@ -62,15 +62,16 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
private TResult Post<TBody, TResult>(string url, TBody content)
{
// note: avoid HttpClient for macOS compatibility
- using WebClient client = new WebClient();
+ using WebClient client = new();
- Uri fullUrl = new Uri(this.BaseUrl, url);
+ Uri fullUrl = new(this.BaseUrl, url);
string data = JsonConvert.SerializeObject(content);
client.Headers["Content-Type"] = "application/json";
client.Headers["User-Agent"] = $"SMAPI/{this.Version}";
string response = client.UploadString(fullUrl, data);
- return JsonConvert.DeserializeObject<TResult>(response, this.JsonSettings);
+ return JsonConvert.DeserializeObject<TResult>(response, this.JsonSettings)
+ ?? throw new InvalidOperationException($"Could not parse the response from POST {url}.");
}
}
}
diff --git a/src/SMAPI.Toolkit/Framework/Clients/Wiki/ChangeDescriptor.cs b/src/SMAPI.Toolkit/Framework/Clients/Wiki/ChangeDescriptor.cs
index f1feb44b..a2497dea 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/Wiki/ChangeDescriptor.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/Wiki/ChangeDescriptor.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
@@ -47,11 +48,19 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
/// <summary>Apply the change descriptors to a comma-delimited field.</summary>
/// <param name="rawField">The raw field text.</param>
/// <returns>Returns the modified field.</returns>
- public string ApplyToCopy(string rawField)
+#if NET5_0_OR_GREATER
+ [return: NotNullIfNotNull("rawField")]
+#endif
+ public string? ApplyToCopy(string? rawField)
{
// get list
List<string> values = !string.IsNullOrWhiteSpace(rawField)
- ? new List<string>(rawField.Split(','))
+ ? new List<string>(
+ from field in rawField.Split(',')
+ let value = field.Trim()
+ where value.Length > 0
+ select value
+ )
: new List<string>();
// apply changes
@@ -73,12 +82,12 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
{
for (int i = values.Count - 1; i >= 0; i--)
{
- string value = this.FormatValue(values[i]?.Trim() ?? string.Empty);
+ string value = this.FormatValue(values[i].Trim());
if (this.Remove.Contains(value))
values.RemoveAt(i);
- else if (this.Replace.TryGetValue(value, out string newValue))
+ else if (this.Replace.TryGetValue(value, out string? newValue))
values[i] = newValue;
}
}
@@ -86,7 +95,7 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
// add values
if (this.Add.Any())
{
- HashSet<string> curValues = new HashSet<string>(values.Select(p => p?.Trim() ?? string.Empty), StringComparer.OrdinalIgnoreCase);
+ HashSet<string> curValues = new HashSet<string>(values.Select(p => p.Trim()), StringComparer.OrdinalIgnoreCase);
foreach (string add in this.Add)
{
if (!curValues.Contains(add))
@@ -119,7 +128,7 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
/// <param name="descriptor">The raw change descriptor.</param>
/// <param name="errors">The human-readable error message describing any invalid values that were ignored.</param>
/// <param name="formatValue">Format a raw value into a normalized form if needed.</param>
- public static ChangeDescriptor Parse(string descriptor, out string[] errors, Func<string, string> formatValue = null)
+ public static ChangeDescriptor Parse(string? descriptor, out string[] errors, Func<string, string>? formatValue = null)
{
// init
formatValue ??= p => p;
@@ -179,7 +188,7 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
errors = rawErrors.ToArray();
}
else
- errors = new string[0];
+ errors = Array.Empty<string>();
// build model
return new ChangeDescriptor(
diff --git a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs
index f85e82e1..7f06d170 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs
@@ -51,8 +51,8 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
doc.LoadHtml(html);
// fetch game versions
- string stableVersion = doc.DocumentNode.SelectSingleNode("//div[@class='game-stable-version']")?.InnerText;
- string betaVersion = doc.DocumentNode.SelectSingleNode("//div[@class='game-beta-version']")?.InnerText;
+ string? stableVersion = doc.DocumentNode.SelectSingleNode("//div[@class='game-stable-version']")?.InnerText;
+ string? betaVersion = doc.DocumentNode.SelectSingleNode("//div[@class='game-beta-version']")?.InnerText;
if (betaVersion == stableVersion)
betaVersion = null;
@@ -63,9 +63,9 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
if (modNodes == null)
throw new InvalidOperationException("Can't parse wiki compatibility list, no mod data overrides section found.");
- foreach (var entry in this.ParseOverrideEntries(modNodes))
+ foreach (WikiDataOverrideEntry entry in this.ParseOverrideEntries(modNodes))
{
- if (entry.Ids?.Any() != true || !entry.HasChanges)
+ if (entry.Ids.Any() != true || !entry.HasChanges)
continue;
foreach (string id in entry.Ids)
@@ -83,18 +83,17 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
}
// build model
- return new WikiModList
- {
- StableVersion = stableVersion,
- BetaVersion = betaVersion,
- Mods = mods
- };
+ return new WikiModList(
+ stableVersion: stableVersion,
+ betaVersion: betaVersion,
+ mods: mods
+ );
}
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
public void Dispose()
{
- this.Client?.Dispose();
+ this.Client.Dispose();
}
@@ -116,71 +115,68 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
int? nexusID = this.GetAttributeAsNullableInt(node, "data-nexus-id");
int? chucklefishID = this.GetAttributeAsNullableInt(node, "data-cf-id");
int? curseForgeID = this.GetAttributeAsNullableInt(node, "data-curseforge-id");
- string curseForgeKey = this.GetAttribute(node, "data-curseforge-key");
+ string? curseForgeKey = this.GetAttribute(node, "data-curseforge-key");
int? modDropID = this.GetAttributeAsNullableInt(node, "data-moddrop-id");
- string githubRepo = this.GetAttribute(node, "data-github");
- string customSourceUrl = this.GetAttribute(node, "data-custom-source");
- string customUrl = this.GetAttribute(node, "data-url");
- string anchor = this.GetAttribute(node, "id");
- string contentPackFor = this.GetAttribute(node, "data-content-pack-for");
- string devNote = this.GetAttribute(node, "data-dev-note");
- string pullRequestUrl = this.GetAttribute(node, "data-pr");
+ string? githubRepo = this.GetAttribute(node, "data-github");
+ string? customSourceUrl = this.GetAttribute(node, "data-custom-source");
+ string? customUrl = this.GetAttribute(node, "data-url");
+ string? anchor = this.GetAttribute(node, "id");
+ string? contentPackFor = this.GetAttribute(node, "data-content-pack-for");
+ string? devNote = this.GetAttribute(node, "data-dev-note");
+ string? pullRequestUrl = this.GetAttribute(node, "data-pr");
// parse stable compatibility
- WikiCompatibilityInfo compatibility = new WikiCompatibilityInfo
- {
- Status = this.GetAttributeAsEnum<WikiCompatibilityStatus>(node, "data-status") ?? WikiCompatibilityStatus.Ok,
- BrokeIn = this.GetAttribute(node, "data-broke-in"),
- UnofficialVersion = this.GetAttributeAsSemanticVersion(node, "data-unofficial-version"),
- UnofficialUrl = this.GetAttribute(node, "data-unofficial-url"),
- Summary = this.GetInnerHtml(node, "mod-summary")?.Trim()
- };
+ WikiCompatibilityInfo compatibility = new(
+ status: this.GetAttributeAsEnum<WikiCompatibilityStatus>(node, "data-status") ?? WikiCompatibilityStatus.Ok,
+ brokeIn: this.GetAttribute(node, "data-broke-in"),
+ unofficialVersion: this.GetAttributeAsSemanticVersion(node, "data-unofficial-version"),
+ unofficialUrl: this.GetAttribute(node, "data-unofficial-url"),
+ summary: this.GetInnerHtml(node, "mod-summary")?.Trim()
+ );
// parse beta compatibility
- WikiCompatibilityInfo betaCompatibility = null;
+ WikiCompatibilityInfo? betaCompatibility = null;
{
WikiCompatibilityStatus? betaStatus = this.GetAttributeAsEnum<WikiCompatibilityStatus>(node, "data-beta-status");
if (betaStatus.HasValue)
{
- betaCompatibility = new WikiCompatibilityInfo
- {
- Status = betaStatus.Value,
- BrokeIn = this.GetAttribute(node, "data-beta-broke-in"),
- UnofficialVersion = this.GetAttributeAsSemanticVersion(node, "data-beta-unofficial-version"),
- UnofficialUrl = this.GetAttribute(node, "data-beta-unofficial-url"),
- Summary = this.GetInnerHtml(node, "mod-beta-summary")
- };
+ betaCompatibility = new WikiCompatibilityInfo(
+ status: betaStatus.Value,
+ brokeIn: this.GetAttribute(node, "data-beta-broke-in"),
+ unofficialVersion: this.GetAttributeAsSemanticVersion(node, "data-beta-unofficial-version"),
+ unofficialUrl: this.GetAttribute(node, "data-beta-unofficial-url"),
+ summary: this.GetInnerHtml(node, "mod-beta-summary")
+ );
}
}
// find data overrides
- WikiDataOverrideEntry overrides = ids
+ WikiDataOverrideEntry? overrides = ids
.Select(id => overridesById.TryGetValue(id, out overrides) ? overrides : null)
.FirstOrDefault(p => p != null);
// yield model
- yield return new WikiModEntry
- {
- ID = ids,
- Name = names,
- Author = authors,
- NexusID = nexusID,
- ChucklefishID = chucklefishID,
- CurseForgeID = curseForgeID,
- CurseForgeKey = curseForgeKey,
- ModDropID = modDropID,
- GitHubRepo = githubRepo,
- CustomSourceUrl = customSourceUrl,
- CustomUrl = customUrl,
- ContentPackFor = contentPackFor,
- Compatibility = compatibility,
- BetaCompatibility = betaCompatibility,
- Warnings = warnings,
- PullRequestUrl = pullRequestUrl,
- DevNote = devNote,
- Overrides = overrides,
- Anchor = anchor
- };
+ yield return new WikiModEntry(
+ id: ids,
+ name: names,
+ author: authors,
+ nexusId: nexusID,
+ chucklefishId: chucklefishID,
+ curseForgeId: curseForgeID,
+ curseForgeKey: curseForgeKey,
+ modDropId: modDropID,
+ githubRepo: githubRepo,
+ customSourceUrl: customSourceUrl,
+ customUrl: customUrl,
+ contentPackFor: contentPackFor,
+ compatibility: compatibility,
+ betaCompatibility: betaCompatibility,
+ warnings: warnings,
+ pullRequestUrl: pullRequestUrl,
+ devNote: devNote,
+ overrides: overrides,
+ anchor: anchor
+ );
}
}
@@ -194,10 +190,10 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
{
Ids = this.GetAttributeAsCsv(node, "data-id"),
ChangeLocalVersions = this.GetAttributeAsChangeDescriptor(node, "data-local-version",
- raw => SemanticVersion.TryParse(raw, out ISemanticVersion version) ? version.ToString() : raw
+ raw => SemanticVersion.TryParse(raw, out ISemanticVersion? version) ? version.ToString() : raw
),
ChangeRemoteVersions = this.GetAttributeAsChangeDescriptor(node, "data-remote-version",
- raw => SemanticVersion.TryParse(raw, out ISemanticVersion version) ? version.ToString() : raw
+ raw => SemanticVersion.TryParse(raw, out ISemanticVersion? version) ? version.ToString() : raw
),
ChangeUpdateKeys = this.GetAttributeAsChangeDescriptor(node, "data-update-keys",
@@ -210,7 +206,7 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
/// <summary>Get an attribute value.</summary>
/// <param name="element">The element whose attributes to read.</param>
/// <param name="name">The attribute name.</param>
- private string GetAttribute(HtmlNode element, string name)
+ private string? GetAttribute(HtmlNode element, string name)
{
string value = element.GetAttributeValue(name, null);
if (string.IsNullOrWhiteSpace(value))
@@ -223,9 +219,9 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
/// <param name="element">The element whose attributes to read.</param>
/// <param name="name">The attribute name.</param>
/// <param name="formatValue">Format an raw entry value when applying changes.</param>
- private ChangeDescriptor GetAttributeAsChangeDescriptor(HtmlNode element, string name, Func<string, string> formatValue)
+ private ChangeDescriptor? GetAttributeAsChangeDescriptor(HtmlNode element, string name, Func<string, string> formatValue)
{
- string raw = this.GetAttribute(element, name);
+ string? raw = this.GetAttribute(element, name);
return raw != null
? ChangeDescriptor.Parse(raw, out _, formatValue)
: null;
@@ -236,10 +232,10 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
/// <param name="name">The attribute name.</param>
private string[] GetAttributeAsCsv(HtmlNode element, string name)
{
- string raw = this.GetAttribute(element, name);
+ string? raw = this.GetAttribute(element, name);
return !string.IsNullOrWhiteSpace(raw)
? raw.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(p => p.Trim()).ToArray()
- : new string[0];
+ : Array.Empty<string>();
}
/// <summary>Get an attribute value and parse it as an enum value.</summary>
@@ -248,7 +244,7 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
/// <param name="name">The attribute name.</param>
private TEnum? GetAttributeAsEnum<TEnum>(HtmlNode element, string name) where TEnum : struct
{
- string raw = this.GetAttribute(element, name);
+ string? raw = this.GetAttribute(element, name);
if (raw == null)
return null;
if (!Enum.TryParse(raw, true, out TEnum value) && Enum.IsDefined(typeof(TEnum), value))
@@ -259,10 +255,10 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
/// <summary>Get an attribute value and parse it as a semantic version.</summary>
/// <param name="element">The element whose attributes to read.</param>
/// <param name="name">The attribute name.</param>
- private ISemanticVersion GetAttributeAsSemanticVersion(HtmlNode element, string name)
+ private ISemanticVersion? GetAttributeAsSemanticVersion(HtmlNode element, string name)
{
- string raw = this.GetAttribute(element, name);
- return SemanticVersion.TryParse(raw, out ISemanticVersion version)
+ string? raw = this.GetAttribute(element, name);
+ return SemanticVersion.TryParse(raw, out ISemanticVersion? version)
? version
: null;
}
@@ -272,7 +268,7 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
/// <param name="name">The attribute name.</param>
private int? GetAttributeAsNullableInt(HtmlNode element, string name)
{
- string raw = this.GetAttribute(element, name);
+ string? raw = this.GetAttribute(element, name);
if (raw != null && int.TryParse(raw, out int value))
return value;
return null;
@@ -281,7 +277,7 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
/// <summary>Get the text of an element with the given class name.</summary>
/// <param name="container">The metadata container.</param>
/// <param name="className">The field name.</param>
- private string GetInnerHtml(HtmlNode container, string className)
+ private string? GetInnerHtml(HtmlNode container, string className)
{
return container.Descendants().FirstOrDefault(p => p.HasClass(className))?.InnerHtml;
}
@@ -291,8 +287,22 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")]
private class ResponseModel
{
+ /*********
+ ** Accessors
+ *********/
/// <summary>The parse API results.</summary>
- public ResponseParseModel Parse { get; set; }
+ public ResponseParseModel Parse { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="parse">The parse API results.</param>
+ public ResponseModel(ResponseParseModel parse)
+ {
+ this.Parse = parse;
+ }
}
/// <summary>The inner response model for the MediaWiki parse API.</summary>
@@ -301,8 +311,11 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")]
private class ResponseParseModel
{
+ /*********
+ ** Accessors
+ *********/
/// <summary>The parsed text.</summary>
- public IDictionary<string, string> Text { get; set; }
+ public IDictionary<string, string> Text { get; } = new Dictionary<string, string>();
}
}
}
diff --git a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityInfo.cs b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityInfo.cs
index 204acd2b..71c90d0c 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityInfo.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityInfo.cs
@@ -7,18 +7,37 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
** Accessors
*********/
/// <summary>The compatibility status.</summary>
- public WikiCompatibilityStatus Status { get; set; }
+ public WikiCompatibilityStatus Status { get; }
/// <summary>The human-readable summary of the compatibility status or workaround, without HTML formatting.</summary>
- public string Summary { get; set; }
+ public string? Summary { get; }
- /// <summary>The game or SMAPI version which broke this mod (if applicable).</summary>
- public string BrokeIn { get; set; }
+ /// <summary>The game or SMAPI version which broke this mod, if applicable.</summary>
+ public string? BrokeIn { get; }
/// <summary>The version of the latest unofficial update, if applicable.</summary>
- public ISemanticVersion UnofficialVersion { get; set; }
+ public ISemanticVersion? UnofficialVersion { get; }
/// <summary>The URL to the latest unofficial update, if applicable.</summary>
- public string UnofficialUrl { get; set; }
+ public string? UnofficialUrl { get; }
+
+
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="status">The compatibility status.</param>
+ /// <param name="summary">The human-readable summary of the compatibility status or workaround, without HTML formatting.</param>
+ /// <param name="brokeIn">The game or SMAPI version which broke this mod, if applicable.</param>
+ /// <param name="unofficialVersion">The version of the latest unofficial update, if applicable.</param>
+ /// <param name="unofficialUrl">The URL to the latest unofficial update, if applicable.</param>
+ public WikiCompatibilityInfo(WikiCompatibilityStatus status, string? summary, string? brokeIn, ISemanticVersion? unofficialVersion, string? unofficialUrl)
+ {
+ this.Status = status;
+ this.Summary = summary;
+ this.BrokeIn = brokeIn;
+ this.UnofficialVersion = unofficialVersion;
+ this.UnofficialUrl = unofficialUrl;
+ }
}
}
diff --git a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiDataOverrideEntry.cs b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiDataOverrideEntry.cs
index 0587e09d..a6f5a88f 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiDataOverrideEntry.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiDataOverrideEntry.cs
@@ -1,4 +1,4 @@
-#nullable enable
+using System;
namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
{
@@ -9,7 +9,7 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
** Accessors
*********/
/// <summary>The unique mod IDs for the mods to override.</summary>
- public string[] Ids { get; set; } = new string[0];
+ public string[] Ids { get; set; } = Array.Empty<string>();
/// <summary>Maps local versions to a semantic version for update checks.</summary>
public ChangeDescriptor? ChangeLocalVersions { get; set; }
diff --git a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModEntry.cs b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModEntry.cs
index 4e0104da..fc50125f 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModEntry.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModEntry.cs
@@ -1,3 +1,5 @@
+using System.Diagnostics.CodeAnalysis;
+
namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
{
/// <summary>A mod entry in the wiki list.</summary>
@@ -6,64 +8,114 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
/*********
** Accessors
*********/
- /// <summary>The mod's unique ID. If the mod has alternate/old IDs, they're listed in latest to newest order.</summary>
- public string[] ID { get; set; }
+ /// <summary>The mod's unique ID. If the mod has alternate/old IDs, they're listed in latest to oldest order.</summary>
+ public string[] ID { get; }
/// <summary>The mod's display name. If the mod has multiple names, the first one is the most canonical name.</summary>
- public string[] Name { get; set; }
+ public string[] Name { get; }
- /// <summary>The mod's author name. If the author has multiple names, the first one is the most canonical name.</summary>
- public string[] Author { get; set; }
+ /// <summary>The mod's author name. If the author has multiple names, the first one is the most canonical name.</summary>
+ public string[] Author { get; }
/// <summary>The mod ID on Nexus.</summary>
- public int? NexusID { get; set; }
+ public int? NexusID { get; }
/// <summary>The mod ID in the Chucklefish mod repo.</summary>
- public int? ChucklefishID { get; set; }
+ public int? ChucklefishID { get; }
/// <summary>The mod ID in the CurseForge mod repo.</summary>
- public int? CurseForgeID { get; set; }
+ public int? CurseForgeID { get; }
/// <summary>The mod key in the CurseForge mod repo (used in mod page URLs).</summary>
- public string CurseForgeKey { get; set; }
+ public string? CurseForgeKey { get; }
/// <summary>The mod ID in the ModDrop mod repo.</summary>
- public int? ModDropID { get; set; }
+ public int? ModDropID { get; }
/// <summary>The GitHub repository in the form 'owner/repo'.</summary>
- public string GitHubRepo { get; set; }
+ public string? GitHubRepo { get; }
/// <summary>The URL to a non-GitHub source repo.</summary>
- public string CustomSourceUrl { get; set; }
+ public string? CustomSourceUrl { get; }
/// <summary>The custom mod page URL (if applicable).</summary>
- public string CustomUrl { get; set; }
+ public string? CustomUrl { get; }
/// <summary>The name of the mod which loads this content pack, if applicable.</summary>
- public string ContentPackFor { get; set; }
+ public string? ContentPackFor { get; }
/// <summary>The mod's compatibility with the latest stable version of the game.</summary>
- public WikiCompatibilityInfo Compatibility { get; set; }
+ public WikiCompatibilityInfo Compatibility { get; }
/// <summary>The mod's compatibility with the latest beta version of the game (if any).</summary>
- public WikiCompatibilityInfo BetaCompatibility { get; set; }
+ public WikiCompatibilityInfo? BetaCompatibility { get; }
/// <summary>Whether a Stardew Valley or SMAPI beta which affects mod compatibility is in progress. If this is true, <see cref="BetaCompatibility"/> should be used for beta versions of SMAPI instead of <see cref="Compatibility"/>.</summary>
+#if NET5_0_OR_GREATER
+ [MemberNotNullWhen(true, nameof(WikiModEntry.BetaCompatibility))]
+#endif
public bool HasBetaInfo => this.BetaCompatibility != null;
/// <summary>The human-readable warnings for players about this mod.</summary>
- public string[] Warnings { get; set; }
+ public string[] Warnings { get; }
/// <summary>The URL of the pull request which submits changes for an unofficial update to the author, if any.</summary>
- public string PullRequestUrl { get; set; }
+ public string? PullRequestUrl { get; }
- /// <summary>Special notes intended for developers who maintain unofficial updates or submit pull requests. </summary>
- public string DevNote { get; set; }
+ /// <summary>Special notes intended for developers who maintain unofficial updates or submit pull requests.</summary>
+ public string? DevNote { get; }
/// <summary>The data overrides to apply to the mod's manifest or remote mod page data, if any.</summary>
- public WikiDataOverrideEntry Overrides { get; set; }
+ public WikiDataOverrideEntry? Overrides { get; }
/// <summary>The link anchor for the mod entry in the wiki compatibility list.</summary>
- public string Anchor { get; set; }
+ public string? Anchor { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="id">The mod's unique ID. If the mod has alternate/old IDs, they're listed in latest to oldest order.</param>
+ /// <param name="name">The mod's display name. If the mod has multiple names, the first one is the most canonical name.</param>
+ /// <param name="author">The mod's author name. If the author has multiple names, the first one is the most canonical name.</param>
+ /// <param name="nexusId">The mod ID on Nexus.</param>
+ /// <param name="chucklefishId">The mod ID in the Chucklefish mod repo.</param>
+ /// <param name="curseForgeId">The mod ID in the CurseForge mod repo.</param>
+ /// <param name="curseForgeKey">The mod ID in the CurseForge mod repo.</param>
+ /// <param name="modDropId">The mod ID in the ModDrop mod repo.</param>
+ /// <param name="githubRepo">The GitHub repository in the form 'owner/repo'.</param>
+ /// <param name="customSourceUrl">The URL to a non-GitHub source repo.</param>
+ /// <param name="customUrl">The custom mod page URL (if applicable).</param>
+ /// <param name="contentPackFor">The name of the mod which loads this content pack, if applicable.</param>
+ /// <param name="compatibility">The mod's compatibility with the latest stable version of the game.</param>
+ /// <param name="betaCompatibility">The mod's compatibility with the latest beta version of the game (if any).</param>
+ /// <param name="warnings">The human-readable warnings for players about this mod.</param>
+ /// <param name="pullRequestUrl">The URL of the pull request which submits changes for an unofficial update to the author, if any.</param>
+ /// <param name="devNote">Special notes intended for developers who maintain unofficial updates or submit pull requests.</param>
+ /// <param name="overrides">The data overrides to apply to the mod's manifest or remote mod page data, if any.</param>
+ /// <param name="anchor">The link anchor for the mod entry in the wiki compatibility list.</param>
+ public WikiModEntry(string[] id, string[] name, string[] author, int? nexusId, int? chucklefishId, int? curseForgeId, string? curseForgeKey, int? modDropId, string? githubRepo, string? customSourceUrl, string? customUrl, string? contentPackFor, WikiCompatibilityInfo compatibility, WikiCompatibilityInfo? betaCompatibility, string[] warnings, string? pullRequestUrl, string? devNote, WikiDataOverrideEntry? overrides, string? anchor)
+ {
+ this.ID = id;
+ this.Name = name;
+ this.Author = author;
+ this.NexusID = nexusId;
+ this.ChucklefishID = chucklefishId;
+ this.CurseForgeID = curseForgeId;
+ this.CurseForgeKey = curseForgeKey;
+ this.ModDropID = modDropId;
+ this.GitHubRepo = githubRepo;
+ this.CustomSourceUrl = customSourceUrl;
+ this.CustomUrl = customUrl;
+ this.ContentPackFor = contentPackFor;
+ this.Compatibility = compatibility;
+ this.BetaCompatibility = betaCompatibility;
+ this.Warnings = warnings;
+ this.PullRequestUrl = pullRequestUrl;
+ this.DevNote = devNote;
+ this.Overrides = overrides;
+ this.Anchor = anchor;
+ }
}
}
diff --git a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModList.cs b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModList.cs
index 0d614f28..24548078 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModList.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModList.cs
@@ -7,12 +7,27 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
** Accessors
*********/
/// <summary>The stable game version.</summary>
- public string StableVersion { get; set; }
+ public string? StableVersion { get; }
/// <summary>The beta game version (if any).</summary>
- public string BetaVersion { get; set; }
+ public string? BetaVersion { get; }
/// <summary>The mods on the wiki.</summary>
- public WikiModEntry[] Mods { get; set; }
+ public WikiModEntry[] Mods { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="stableVersion">The stable game version.</param>
+ /// <param name="betaVersion">The beta game version (if any).</param>
+ /// <param name="mods">The mods on the wiki.</param>
+ public WikiModList(string? stableVersion, string? betaVersion, WikiModEntry[] mods)
+ {
+ this.StableVersion = stableVersion;
+ this.BetaVersion = betaVersion;
+ this.Mods = mods;
+ }
}
}