From e2e7e096b7f62eb6a5145970ecac3b7edc0bfef1 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 8 Oct 2017 02:03:55 -0400 Subject: handle various edge cases in manifest parsing for zip filename --- .../Tasks/CreateModReleaseZip.cs | 69 +++++++++++++++++----- 1 file changed, 53 insertions(+), 16 deletions(-) (limited to 'src/SMAPI.ModBuildConfig') diff --git a/src/SMAPI.ModBuildConfig/Tasks/CreateModReleaseZip.cs b/src/SMAPI.ModBuildConfig/Tasks/CreateModReleaseZip.cs index 01053860..c8582488 100644 --- a/src/SMAPI.ModBuildConfig/Tasks/CreateModReleaseZip.cs +++ b/src/SMAPI.ModBuildConfig/Tasks/CreateModReleaseZip.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; +using System.Linq; using System.Web.Script.Serialization; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; @@ -11,6 +12,13 @@ namespace StardewModdingAPI.ModBuildConfig.Tasks /// A build task which packs mod files into a conventional release zip. public class CreateModReleaseZip : Task { + /********* + ** Properties + *********/ + /// The name of the manifest file. + private readonly string ManifestFileName = "manifest.json"; + + /********* ** Accessors *********/ @@ -30,6 +38,8 @@ namespace StardewModdingAPI.ModBuildConfig.Tasks /********* ** Public methods *********/ + /// When overridden in a derived class, executes the task. + /// true if the task successfully executed; otherwise, false. public override bool Execute() { try @@ -38,7 +48,7 @@ namespace StardewModdingAPI.ModBuildConfig.Tasks Directory.CreateDirectory(this.OutputFolderPath); // get zip filename - string fileName = string.Format("{0}-{1}.zip", this.ModName, this.GetManifestVersion()); + string fileName = $"{this.ModName}-{this.GetManifestVersion()}.zip"; // clear old zip file if present string zipPath = Path.Combine(this.OutputFolderPath, fileName); @@ -76,28 +86,55 @@ namespace StardewModdingAPI.ModBuildConfig.Tasks } /// Get a semantic version from the mod manifest (if available). + /// The manifest file wasn't found or is invalid. public string GetManifestVersion() { - // Get the file JSON string - string json = ""; - foreach (ITaskItem file in this.Files) + // find manifest file + ITaskItem file = this.Files.FirstOrDefault(p => this.ManifestFileName.Equals(Path.GetFileName(p.ItemSpec), StringComparison.InvariantCultureIgnoreCase)); + if (file == null) + throw new InvalidOperationException($"The mod must include a {this.ManifestFileName} file."); + + // read content + string json = File.ReadAllText(file.ItemSpec); + if (string.IsNullOrWhiteSpace(json)) + throw new InvalidOperationException($"The mod's {this.ManifestFileName} file must not be empty."); + + // parse JSON + IDictionary data; + try + { + data = this.Parse(json); + } + catch (Exception ex) { - if (Path.GetFileName(file.ItemSpec).ToLower() != "manifest.json") - continue; - json = File.ReadAllText(file.ItemSpec); - break; + throw new InvalidOperationException($"The mod's {this.ManifestFileName} couldn't be parsed. It doesn't seem to be valid JSON.", ex); } - // Serialize the manifest json into a data object, then get a version object from that. - IDictionary data = (IDictionary)new JavaScriptSerializer().DeserializeObject(json); - IDictionary version = (IDictionary)data["Version"]; + // extract version dictionary + IDictionary versionFields = (IDictionary)data["Version"]; + int major = versionFields.ContainsKey("MajorVersion") ? (int)versionFields["MajorVersion"] : 0; + int minor = versionFields.ContainsKey("MinorVersion") ? (int)versionFields["MinorVersion"] : 0; + int patch = versionFields.ContainsKey("PatchVersion") ? (int)versionFields["PatchVersion"] : 0; - // Store our version numbers for ease of use - int major = (int)version["MajorVersion"]; - int minor = (int)version["MinorVersion"]; - int patch = (int)version["PatchVersion"]; + return $"{major}.{minor}.{patch}"; + } - return String.Format("{0}.{1}.{2}", major, minor, patch); + /// Get a case-insensitive dictionary matching the given JSON. + /// The JSON to parse. + private IDictionary Parse(string json) + { + IDictionary MakeCaseInsensitive(IDictionary dict) + { + foreach (var field in dict.ToArray()) + { + if (field.Value is IDictionary value) + dict[field.Key] = MakeCaseInsensitive(value); + } + return new Dictionary(dict, StringComparer.InvariantCultureIgnoreCase); + } + + IDictionary data = (IDictionary)new JavaScriptSerializer().DeserializeObject(json); + return MakeCaseInsensitive(data); } } } -- cgit