diff options
author | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2022-11-10 23:34:50 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-10 23:34:50 -0500 |
commit | eaacfd04b8d526d9d190c864231f5f365e19a7da (patch) | |
tree | 706a6ef8de9b2112233bcf274be1d1cbe3e8c945 /src/SMAPI.Toolkit/Framework/ManifestValidator.cs | |
parent | 2a8cb8c636f0b672284b8daffcfdd5bec36c00f9 (diff) | |
parent | 867afdd96ff8896dc81fdab204cf045713d32d91 (diff) | |
download | SMAPI-eaacfd04b8d526d9d190c864231f5f365e19a7da.tar.gz SMAPI-eaacfd04b8d526d9d190c864231f5f365e19a7da.tar.bz2 SMAPI-eaacfd04b8d526d9d190c864231f5f365e19a7da.zip |
Merge pull request #881 from tylergibbs2/detailed-manifest-errors
Add detailed manifest validation errors at build time
Diffstat (limited to 'src/SMAPI.Toolkit/Framework/ManifestValidator.cs')
-rw-r--r-- | src/SMAPI.Toolkit/Framework/ManifestValidator.cs | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/src/SMAPI.Toolkit/Framework/ManifestValidator.cs b/src/SMAPI.Toolkit/Framework/ManifestValidator.cs new file mode 100644 index 00000000..461dc325 --- /dev/null +++ b/src/SMAPI.Toolkit/Framework/ManifestValidator.cs @@ -0,0 +1,106 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using StardewModdingAPI.Toolkit.Utilities; + +namespace StardewModdingAPI.Toolkit.Framework +{ + /// <summary>Validates manifest fields.</summary> + public static class ManifestValidator + { + /// <summary>Validate a manifest's fields.</summary> + /// <param name="manifest">The manifest to validate.</param> + /// <param name="error">The error message indicating why validation failed, if applicable.</param> + /// <returns>Returns whether all manifest fields validated successfully.</returns> + [SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract", Justification = "This is the method that ensures those annotations are respected.")] + public static bool TryValidateFields(IManifest manifest, out string error) + { + // + // 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 isContentPack = manifest.ContentPackFor != null; + + // validate use of EntryDll vs ContentPackFor fields + if (hasDll == isContentPack) + { + error = hasDll + ? $"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."; + return false; + } + + // validate EntryDll/ContentPackFor format + if (hasDll) + { + if (manifest.EntryDll!.Intersect(Path.GetInvalidFileNameChars()).Any()) + { + error = $"manifest has invalid filename '{manifest.EntryDll}' for the {nameof(IManifest.EntryDll)} field."; + return false; + } + } + else + { + if (string.IsNullOrWhiteSpace(manifest.ContentPackFor!.UniqueID)) + { + error = $"manifest declares {nameof(IManifest.ContentPackFor)} without its required {nameof(IManifestContentPackFor.UniqueID)} field."; + return false; + } + } + + // validate required fields + { + List<string> missingFields = new List<string>(3); + + if (string.IsNullOrWhiteSpace(manifest.Name)) + missingFields.Add(nameof(IManifest.Name)); + if (manifest.Version == null || manifest.Version.ToString() == "0.0.0") + missingFields.Add(nameof(IManifest.Version)); + if (string.IsNullOrWhiteSpace(manifest.UniqueID)) + missingFields.Add(nameof(IManifest.UniqueID)); + + if (missingFields.Any()) + { + error = $"manifest is missing required fields ({string.Join(", ", missingFields)})."; + return false; + } + } + + // validate ID format + if (!PathUtilities.IsSlug(manifest.UniqueID)) + { + error = "manifest specifies an invalid ID (IDs must only contain letters, numbers, underscores, periods, or hyphens)."; + return false; + } + + // validate dependency format + foreach (IManifestDependency? dependency in manifest.Dependencies) + { + if (dependency == null) + { + error = $"manifest has a null entry under {nameof(IManifest.Dependencies)}."; + return false; + } + + if (string.IsNullOrWhiteSpace(dependency.UniqueID)) + { + error = $"manifest has a {nameof(IManifest.Dependencies)} entry with no {nameof(IManifestDependency.UniqueID)} field."; + return false; + } + + 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)."; + return false; + } + } + + error = ""; + return true; + } + } +} |