tweak new code

This commit is contained in:
Jesse Plamondon-Willard 2022-11-10 23:27:38 -05:00
parent 6ee0d2f93d
commit 867afdd96f
No known key found for this signature in database
GPG Key ID: CF8B1456B3E29F49
3 changed files with 51 additions and 49 deletions

View File

@ -85,33 +85,30 @@ namespace StardewModdingAPI.ModBuildConfig
return true; return true;
// validate the manifest file // validate the manifest file
Manifest manifest; IManifest manifest;
{ {
// check if manifest file exists
FileInfo manifestFile = new(Path.Combine(this.ProjectDir, "manifest.json"));
if (!manifestFile.Exists)
{
this.Log.LogError("[mod build package] The mod does not have a manifest.json file.");
return false;
}
// check if the json is valid
try try
{ {
new JsonHelper().ReadJsonFileIfExists(manifestFile.FullName, out manifest); string manifestPath = Path.Combine(this.ProjectDir, "manifest.json");
if (!new JsonHelper().ReadJsonFileIfExists(manifestPath, out Manifest rawManifest))
{
this.Log.LogError("[mod build package] The mod's manifest.json file doesn't exist.");
return false;
}
manifest = rawManifest;
} }
catch (JsonReaderException ex) catch (JsonReaderException ex)
{ {
// log the inner exception, otherwise the message will be generic // log the inner exception, otherwise the message will be generic
Exception exToShow = ex.InnerException ?? ex; Exception exToShow = ex.InnerException ?? ex;
this.Log.LogError($"[mod build package] Failed to parse manifest.json: {exToShow.Message}"); this.Log.LogError($"[mod build package] The mod's manifest.json file isn't valid JSON: {exToShow.Message}");
return false; return false;
} }
// validate the manifest's fields // validate manifest fields
if (!ManifestValidator.TryValidate(manifest, out string error)) if (!ManifestValidator.TryValidateFields(manifest, out string error))
{ {
this.Log.LogError($"[mod build package] The mod manifest is invalid: {error}"); this.Log.LogError($"[mod build package] The mod's manifest.json file is invalid: {error}");
return false; return false;
} }
} }

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using StardewModdingAPI.Toolkit.Utilities; using StardewModdingAPI.Toolkit.Utilities;
@ -8,40 +9,47 @@ namespace StardewModdingAPI.Toolkit.Framework
/// <summary>Validates manifest fields.</summary> /// <summary>Validates manifest fields.</summary>
public static class ManifestValidator public static class ManifestValidator
{ {
/// <summary>Try to validate a manifest's fields. Fails if any invalid field is found.</summary> /// <summary>Validate a manifest's fields.</summary>
/// <param name="manifest">The manifest to validate.</param> /// <param name="manifest">The manifest to validate.</param>
/// <param name="error">The error message to display to the user.</param> /// <param name="error">The error message indicating why validation failed, if applicable.</param>
/// <returns>Returns whether the manifest was validated successfully.</returns> /// <returns>Returns whether all manifest fields validated successfully.</returns>
public static bool TryValidate(IManifest manifest, out string error) [SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract", Justification = "This is the method that ensures those annotations are respected.")]
public static bool TryValidateFields(IManifest manifest, out string error)
{ {
// validate DLL / content pack fields //
// Note: SMAPI assumes that it can grammatically append the returned sentence in the
// form "failed loading <mod> because its <error>". Any errors returned should be valid
// in that format, unless the SMAPI call is adjusted accordingly.
//
bool hasDll = !string.IsNullOrWhiteSpace(manifest.EntryDll); bool hasDll = !string.IsNullOrWhiteSpace(manifest.EntryDll);
bool isContentPack = manifest.ContentPackFor != null; bool isContentPack = manifest.ContentPackFor != null;
// validate field presence // validate use of EntryDll vs ContentPackFor fields
if (!hasDll && !isContentPack) if (hasDll == isContentPack)
{ {
error = $"manifest has no {nameof(IManifest.EntryDll)} or {nameof(IManifest.ContentPackFor)} field; must specify one."; error = hasDll
return false; ? $"manifest sets both {nameof(IManifest.EntryDll)} and {nameof(IManifest.ContentPackFor)}, which are mutually exclusive."
} : $"manifest has no {nameof(IManifest.EntryDll)} or {nameof(IManifest.ContentPackFor)} field; must specify one.";
if (hasDll && isContentPack)
{
error = $"manifest sets both {nameof(IManifest.EntryDll)} and {nameof(IManifest.ContentPackFor)}, which are mutually exclusive.";
return false; return false;
} }
// validate DLL filename format // validate EntryDll/ContentPackFor format
if (hasDll && manifest.EntryDll!.Intersect(Path.GetInvalidFileNameChars()).Any()) if (hasDll)
{ {
error = $"manifest has invalid filename '{manifest.EntryDll}' for the EntryDLL field."; if (manifest.EntryDll!.Intersect(Path.GetInvalidFileNameChars()).Any())
return false; {
error = $"manifest has invalid filename '{manifest.EntryDll}' for the {nameof(IManifest.EntryDll)} field.";
return false;
}
} }
else
// validate content pack ID
else if (isContentPack && string.IsNullOrWhiteSpace(manifest.ContentPackFor!.UniqueID))
{ {
error = $"manifest declares {nameof(IManifest.ContentPackFor)} without its required {nameof(IManifestContentPackFor.UniqueID)} field."; if (string.IsNullOrWhiteSpace(manifest.ContentPackFor!.UniqueID))
return false; {
error = $"manifest declares {nameof(IManifest.ContentPackFor)} without its required {nameof(IManifestContentPackFor.UniqueID)} field.";
return false;
}
} }
// validate required fields // validate required fields
@ -69,24 +77,21 @@ namespace StardewModdingAPI.Toolkit.Framework
return false; return false;
} }
// validate dependencies // validate dependency format
foreach (IManifestDependency? dependency in manifest.Dependencies) foreach (IManifestDependency? dependency in manifest.Dependencies)
{ {
// null dependency
if (dependency == null) if (dependency == null)
{ {
error = $"manifest has a null entry under {nameof(IManifest.Dependencies)}."; error = $"manifest has a null entry under {nameof(IManifest.Dependencies)}.";
return false; return false;
} }
// missing ID
if (string.IsNullOrWhiteSpace(dependency.UniqueID)) if (string.IsNullOrWhiteSpace(dependency.UniqueID))
{ {
error = $"manifest has a {nameof(IManifest.Dependencies)} entry with no {nameof(IManifestDependency.UniqueID)} field."; error = $"manifest has a {nameof(IManifest.Dependencies)} entry with no {nameof(IManifestDependency.UniqueID)} field.";
return false; return false;
} }
// invalid ID
if (!PathUtilities.IsSlug(dependency.UniqueID)) if (!PathUtilities.IsSlug(dependency.UniqueID))
{ {
error = $"manifest has a {nameof(IManifest.Dependencies)} entry with an invalid {nameof(IManifestDependency.UniqueID)} field (IDs must only contain letters, numbers, underscores, periods, or hyphens)."; error = $"manifest has a {nameof(IManifest.Dependencies)} entry with an invalid {nameof(IManifestDependency.UniqueID)} field (IDs must only contain letters, numbers, underscores, periods, or hyphens).";

View File

@ -126,7 +126,14 @@ namespace StardewModdingAPI.Framework.ModLoading
continue; continue;
} }
// check for dll if it's supposed to have one // validate manifest format
if (!ManifestValidator.TryValidateFields(mod.Manifest, out string manifestError))
{
mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.InvalidManifest, $"its {manifestError}");
continue;
}
// check that DLL exists if applicable
if (!string.IsNullOrEmpty(mod.Manifest.EntryDll) && validateFilesExist) if (!string.IsNullOrEmpty(mod.Manifest.EntryDll) && validateFilesExist)
{ {
IFileLookup pathLookup = getFileLookup(mod.DirectoryPath); IFileLookup pathLookup = getFileLookup(mod.DirectoryPath);
@ -137,13 +144,6 @@ namespace StardewModdingAPI.Framework.ModLoading
continue; continue;
} }
} }
// validate manifest
if (!ManifestValidator.TryValidate(mod.Manifest, out string manifestError))
{
mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.InvalidManifest, $"its {manifestError}");
continue;
}
} }
// validate IDs are unique // validate IDs are unique