diff options
-rw-r--r-- | src/SMAPI.Web/Controllers/LogParserController.cs | 8 | ||||
-rw-r--r-- | src/SMAPI.Web/Framework/LogParsing/LogMessageBuilder.cs | 25 | ||||
-rw-r--r-- | src/SMAPI.Web/Framework/LogParsing/LogParseException.cs | 2 | ||||
-rw-r--r-- | src/SMAPI.Web/Framework/LogParsing/LogParser.cs | 54 | ||||
-rw-r--r-- | src/SMAPI.Web/Framework/LogParsing/Models/LogMessage.cs | 38 | ||||
-rw-r--r-- | src/SMAPI.Web/Framework/LogParsing/Models/LogModInfo.cs | 71 | ||||
-rw-r--r-- | src/SMAPI.Web/Framework/LogParsing/Models/ParsedLog.cs | 20 | ||||
-rw-r--r-- | src/SMAPI.Web/ViewModels/LogParserModel.cs | 51 | ||||
-rw-r--r-- | src/SMAPI.Web/Views/LogParser/Index.cshtml | 21 |
9 files changed, 185 insertions, 105 deletions
diff --git a/src/SMAPI.Web/Controllers/LogParserController.cs b/src/SMAPI.Web/Controllers/LogParserController.cs index 524cfbcc..93f2613e 100644 --- a/src/SMAPI.Web/Controllers/LogParserController.cs +++ b/src/SMAPI.Web/Controllers/LogParserController.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Linq; using System.Text; @@ -47,7 +45,7 @@ namespace StardewModdingAPI.Web.Controllers [HttpGet] [Route("log")] [Route("log/{id}")] - public async Task<ActionResult> Index(string id = null, LogViewFormat format = LogViewFormat.Default, bool renew = false) + public async Task<ActionResult> Index(string? id = null, LogViewFormat format = LogViewFormat.Default, bool renew = false) { // fresh page if (string.IsNullOrWhiteSpace(id)) @@ -89,7 +87,7 @@ namespace StardewModdingAPI.Web.Controllers public async Task<ActionResult> PostAsync() { // get raw log text - string input = this.Request.Form["input"].FirstOrDefault(); + string? input = this.Request.Form["input"].FirstOrDefault(); if (string.IsNullOrWhiteSpace(input)) return this.View("Index", this.GetModel(null, uploadError: "The log file seems to be empty.")); @@ -111,7 +109,7 @@ namespace StardewModdingAPI.Web.Controllers /// <param name="expiry">When the uploaded file will no longer be available.</param> /// <param name="uploadWarning">A non-blocking warning while uploading the log.</param> /// <param name="uploadError">An error which occurred while uploading the log.</param> - private LogParserModel GetModel(string pasteID, DateTime? expiry = null, string uploadWarning = null, string uploadError = null) + private LogParserModel GetModel(string? pasteID, DateTime? expiry = null, string? uploadWarning = null, string? uploadError = null) { Platform? platform = this.DetectClientPlatform(); diff --git a/src/SMAPI.Web/Framework/LogParsing/LogMessageBuilder.cs b/src/SMAPI.Web/Framework/LogParsing/LogMessageBuilder.cs index 1b692e63..a1384b8f 100644 --- a/src/SMAPI.Web/Framework/LogParsing/LogMessageBuilder.cs +++ b/src/SMAPI.Web/Framework/LogParsing/LogMessageBuilder.cs @@ -1,6 +1,5 @@ -#nullable disable - using System; +using System.Diagnostics.CodeAnalysis; using System.Text; using StardewModdingAPI.Web.Framework.LogParsing.Models; @@ -13,7 +12,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing ** Fields *********/ /// <summary>The local time when the next log was posted.</summary> - public string Time { get; set; } + public string? Time { get; set; } /// <summary>The log level for the next log message.</summary> public LogLevel Level { get; set; } @@ -22,7 +21,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing public int ScreenId { get; set; } /// <summary>The mod name for the next log message.</summary> - public string Mod { get; set; } + public string? Mod { get; set; } /// <summary>The text for the next log message.</summary> private readonly StringBuilder Text = new(); @@ -32,6 +31,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing ** Accessors *********/ /// <summary>Whether the next log message has been started.</summary> + [MemberNotNullWhen(true, nameof(LogMessageBuilder.Time), nameof(LogMessageBuilder.Mod))] public bool Started { get; private set; } @@ -72,19 +72,18 @@ namespace StardewModdingAPI.Web.Framework.LogParsing } /// <summary>Get a log message for the accumulated values.</summary> - public LogMessage Build() + public LogMessage? Build() { if (!this.Started) return null; - return new LogMessage - { - Time = this.Time, - Level = this.Level, - ScreenId = this.ScreenId, - Mod = this.Mod, - Text = this.Text.ToString() - }; + return new LogMessage( + time: this.Time, + level: this.Level, + screenId: this.ScreenId, + mod: this.Mod, + text: this.Text.ToString() + ); } /// <summary>Reset to start a new log message.</summary> diff --git a/src/SMAPI.Web/Framework/LogParsing/LogParseException.cs b/src/SMAPI.Web/Framework/LogParsing/LogParseException.cs index 4ee58433..3f815e3e 100644 --- a/src/SMAPI.Web/Framework/LogParsing/LogParseException.cs +++ b/src/SMAPI.Web/Framework/LogParsing/LogParseException.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; namespace StardewModdingAPI.Web.Framework.LogParsing diff --git a/src/SMAPI.Web/Framework/LogParsing/LogParser.cs b/src/SMAPI.Web/Framework/LogParsing/LogParser.cs index 4e61ac95..55272b23 100644 --- a/src/SMAPI.Web/Framework/LogParsing/LogParser.cs +++ b/src/SMAPI.Web/Framework/LogParsing/LogParser.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.IO; @@ -55,7 +53,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing *********/ /// <summary>Parse SMAPI log text.</summary> /// <param name="logText">The SMAPI log text.</param> - public ParsedLog Parse(string logText) + public ParsedLog Parse(string? logText) { try { @@ -79,8 +77,8 @@ namespace StardewModdingAPI.Web.Framework.LogParsing }; // parse log messages - LogModInfo smapiMod = new() { Name = "SMAPI", Author = "Pathoschild", Description = "", Loaded = true }; - LogModInfo gameMod = new() { Name = "game", Author = "", Description = "", Loaded = true }; + LogModInfo smapiMod = new(name: "SMAPI", author: "Pathoschild", version: "", description: "", loaded: true); + LogModInfo gameMod = new(name: "game", author: "", version: "", description: "", loaded: true); IDictionary<string, List<LogModInfo>> mods = new Dictionary<string, List<LogModInfo>>(); bool inModList = false; bool inContentPackList = false; @@ -103,7 +101,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing default: if (mods.TryGetValue(message.Mod, out var entries)) { - foreach (var entry in entries) + foreach (LogModInfo entry in entries) entry.Errors++; } break; @@ -133,9 +131,9 @@ namespace StardewModdingAPI.Web.Framework.LogParsing string author = match.Groups["author"].Value; string description = match.Groups["description"].Value; - if (!mods.TryGetValue(name, out List<LogModInfo> entries)) + if (!mods.TryGetValue(name, out List<LogModInfo>? entries)) mods[name] = entries = new List<LogModInfo>(); - entries.Add(new LogModInfo { Name = name, Author = author, Version = version, Description = description, Loaded = true }); + entries.Add(new LogModInfo(name: name, author: author, version: version, description: description, loaded: true)); message.Section = LogSection.ModsList; } @@ -156,9 +154,9 @@ namespace StardewModdingAPI.Web.Framework.LogParsing string description = match.Groups["description"].Value; string forMod = match.Groups["for"].Value; - if (!mods.TryGetValue(name, out List<LogModInfo> entries)) + if (!mods.TryGetValue(name, out List<LogModInfo>? entries)) mods[name] = entries = new List<LogModInfo>(); - entries.Add(new LogModInfo { Name = name, Author = author, Version = version, Description = description, ContentPackFor = forMod, Loaded = true }); + entries.Add(new LogModInfo(name: name, author: author, version: version, description: description, contentPackFor: forMod, loaded: true)); message.Section = LogSection.ContentPackList; } @@ -179,23 +177,19 @@ namespace StardewModdingAPI.Web.Framework.LogParsing if (mods.TryGetValue(name, out var entries)) { - foreach (var entry in entries) - { - entry.UpdateLink = link; - entry.UpdateVersion = version; - } + foreach (LogModInfo entry in entries) + entry.SetUpdate(version, link); } message.Section = LogSection.ModUpdateList; } - else if (message.Level == LogLevel.Alert && this.SmapiUpdatePattern.IsMatch(message.Text)) { Match match = this.SmapiUpdatePattern.Match(message.Text); string version = match.Groups["version"].Value; string link = match.Groups["link"].Value; - smapiMod.UpdateVersion = version; - smapiMod.UpdateLink = link; + + smapiMod.SetUpdate(version, link); } // platform info line @@ -205,7 +199,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing log.ApiVersion = match.Groups["apiVersion"].Value; log.GameVersion = match.Groups["gameVersion"].Value; log.OperatingSystem = match.Groups["os"].Value; - smapiMod.Version = log.ApiVersion; + smapiMod.OverrideVersion(log.ApiVersion); } // mod path line @@ -215,7 +209,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing log.ModPath = match.Groups["path"].Value; int lastDelimiterPos = log.ModPath.LastIndexOfAny(new[] { '/', '\\' }); log.GamePath = lastDelimiterPos >= 0 - ? log.ModPath.Substring(0, lastDelimiterPos) + ? log.ModPath[..lastDelimiterPos] : log.ModPath; } @@ -229,7 +223,8 @@ namespace StardewModdingAPI.Web.Framework.LogParsing } // finalize log - gameMod.Version = log.GameVersion; + if (log.GameVersion != null) + gameMod.OverrideVersion(log.GameVersion); log.Mods = new[] { gameMod, smapiMod }.Concat(mods.Values.SelectMany(p => p).OrderBy(p => p.Name)).ToArray(); return log; } @@ -261,7 +256,8 @@ namespace StardewModdingAPI.Web.Framework.LogParsing /// <param name="messages">The messages to filter.</param> private IEnumerable<LogMessage> CollapseRepeats(IEnumerable<LogMessage> messages) { - LogMessage next = null; + LogMessage? next = null; + foreach (LogMessage message in messages) { // new message @@ -282,7 +278,9 @@ namespace StardewModdingAPI.Web.Framework.LogParsing yield return next; next = message; } - yield return next; + + if (next != null) + yield return next; } /// <summary>Split a SMAPI log into individual log messages.</summary> @@ -295,7 +293,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing while (true) { // read line - string line = reader.ReadLine(); + string? line = reader.ReadLine(); if (line == null) break; @@ -308,17 +306,17 @@ namespace StardewModdingAPI.Web.Framework.LogParsing { if (builder.Started) { - yield return builder.Build(); + yield return builder.Build()!; builder.Clear(); } - var screenGroup = header.Groups["screen"]; + Group screenGroup = header.Groups["screen"]; builder.Start( time: header.Groups["time"].Value, level: Enum.Parse<LogLevel>(header.Groups["level"].Value, ignoreCase: true), screenId: screenGroup.Success ? int.Parse(screenGroup.Value) : 0, // main player is always screen ID 0 mod: header.Groups["modName"].Value, - text: line.Substring(header.Length) + text: line[header.Length..] ); } else @@ -332,7 +330,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing // end last message if (builder.Started) - yield return builder.Build(); + yield return builder.Build()!; } } } diff --git a/src/SMAPI.Web/Framework/LogParsing/Models/LogMessage.cs b/src/SMAPI.Web/Framework/LogParsing/Models/LogMessage.cs index 57d28755..7a5f32e0 100644 --- a/src/SMAPI.Web/Framework/LogParsing/Models/LogMessage.cs +++ b/src/SMAPI.Web/Framework/LogParsing/Models/LogMessage.cs @@ -1,4 +1,4 @@ -#nullable disable +using System.Diagnostics.CodeAnalysis; namespace StardewModdingAPI.Web.Framework.LogParsing.Models { @@ -9,19 +9,19 @@ namespace StardewModdingAPI.Web.Framework.LogParsing.Models ** Accessors *********/ /// <summary>The local time when the log was posted.</summary> - public string Time { get; set; } + public string Time { get; } /// <summary>The log level.</summary> - public LogLevel Level { get; set; } + public LogLevel Level { get; } /// <summary>The screen ID in split-screen mode.</summary> - public int ScreenId { get; set; } + public int ScreenId { get; } /// <summary>The mod name.</summary> - public string Mod { get; set; } + public string Mod { get; } /// <summary>The log text.</summary> - public string Text { get; set; } + public string Text { get; } /// <summary>The number of times this message was repeated consecutively.</summary> public int Repeated { get; set; } @@ -30,6 +30,32 @@ namespace StardewModdingAPI.Web.Framework.LogParsing.Models public LogSection? Section { get; set; } /// <summary>Whether this message is the first one of its section.</summary> + [MemberNotNullWhen(true, nameof(LogMessage.Section))] public bool IsStartOfSection { get; set; } + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance/</summary> + /// <param name="time">The local time when the log was posted.</param> + /// <param name="level">The log level.</param> + /// <param name="screenId">The screen ID in split-screen mode.</param> + /// <param name="mod">The mod name.</param> + /// <param name="text">The log text.</param> + /// <param name="repeated">The number of times this message was repeated consecutively.</param> + /// <param name="section">The section that this log message belongs to.</param> + /// <param name="isStartOfSection">Whether this message is the first one of its section.</param> + public LogMessage(string time, LogLevel level, int screenId, string mod, string text, int repeated = 0, LogSection? section = null, bool isStartOfSection = false) + { + this.Time = time; + this.Level = level; + this.ScreenId = screenId; + this.Mod = mod; + this.Text = text; + this.Repeated = repeated; + this.Section = section; + this.IsStartOfSection = isStartOfSection; + } } } diff --git a/src/SMAPI.Web/Framework/LogParsing/Models/LogModInfo.cs b/src/SMAPI.Web/Framework/LogParsing/Models/LogModInfo.cs index 349312df..a6b9165c 100644 --- a/src/SMAPI.Web/Framework/LogParsing/Models/LogModInfo.cs +++ b/src/SMAPI.Web/Framework/LogParsing/Models/LogModInfo.cs @@ -1,4 +1,4 @@ -#nullable disable +using System.Diagnostics.CodeAnalysis; namespace StardewModdingAPI.Web.Framework.LogParsing.Models { @@ -9,36 +9,81 @@ namespace StardewModdingAPI.Web.Framework.LogParsing.Models ** Accessors *********/ /// <summary>The mod name.</summary> - public string Name { get; set; } + public string Name { get; } /// <summary>The mod author.</summary> - public string Author { get; set; } - - /// <summary>The update version.</summary> - public string UpdateVersion { get; set; } - - /// <summary>The update link.</summary> - public string UpdateLink { get; set; } + public string Author { get; } /// <summary>The mod version.</summary> - public string Version { get; set; } + public string Version { get; private set; } /// <summary>The mod description.</summary> - public string Description { get; set; } + public string Description { get; } + + /// <summary>The update version.</summary> + public string? UpdateVersion { get; private set; } + + /// <summary>The update link.</summary> + public string? UpdateLink { get; private set; } /// <summary>The name of the mod for which this is a content pack (if applicable).</summary> - public string ContentPackFor { get; set; } + public string? ContentPackFor { get; } /// <summary>The number of errors logged by this mod.</summary> public int Errors { get; set; } /// <summary>Whether the mod was loaded into the game.</summary> - public bool Loaded { get; set; } + public bool Loaded { get; } /// <summary>Whether the mod has an update available.</summary> + [MemberNotNullWhen(true, nameof(LogModInfo.UpdateVersion), nameof(LogModInfo.UpdateLink))] public bool HasUpdate => this.UpdateVersion != null && this.Version != this.UpdateVersion; /// <summary>Whether the mod is a content pack for another mod.</summary> + [MemberNotNullWhen(true, nameof(LogModInfo.ContentPackFor))] public bool IsContentPack => !string.IsNullOrWhiteSpace(this.ContentPackFor); + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="name">The mod name.</param> + /// <param name="author">The mod author.</param> + /// <param name="version">The mod version.</param> + /// <param name="description">The mod description.</param> + /// <param name="updateVersion">The update version.</param> + /// <param name="updateLink">The update link.</param> + /// <param name="contentPackFor">The name of the mod for which this is a content pack (if applicable).</param> + /// <param name="errors">The number of errors logged by this mod.</param> + /// <param name="loaded">Whether the mod was loaded into the game.</param> + public LogModInfo(string name, string author, string version, string description, string? updateVersion = null, string? updateLink = null, string? contentPackFor = null, int errors = 0, bool loaded = true) + { + this.Name = name; + this.Author = author; + this.Version = version; + this.Description = description; + this.UpdateVersion = updateVersion; + this.UpdateLink = updateLink; + this.ContentPackFor = contentPackFor; + this.Errors = errors; + this.Loaded = loaded; + } + + /// <summary>Add an update alert for this mod.</summary> + /// <param name="updateVersion">The update version.</param> + /// <param name="updateLink">The update link.</param> + public void SetUpdate(string updateVersion, string updateLink) + { + this.UpdateVersion = updateVersion; + this.UpdateLink = updateLink; + } + + /// <summary>Override the version number, for cases like SMAPI itself where the version is only known later during parsing.</summary> + /// <param name="version">The new mod version.</param> + public void OverrideVersion(string version) + { + this.Version = version; + } } } diff --git a/src/SMAPI.Web/Framework/LogParsing/Models/ParsedLog.cs b/src/SMAPI.Web/Framework/LogParsing/Models/ParsedLog.cs index dae91d84..6951e434 100644 --- a/src/SMAPI.Web/Framework/LogParsing/Models/ParsedLog.cs +++ b/src/SMAPI.Web/Framework/LogParsing/Models/ParsedLog.cs @@ -1,6 +1,5 @@ -#nullable disable - using System; +using System.Diagnostics.CodeAnalysis; namespace StardewModdingAPI.Web.Framework.LogParsing.Models { @@ -14,31 +13,32 @@ namespace StardewModdingAPI.Web.Framework.LogParsing.Models ** Metadata ****/ /// <summary>Whether the log file was successfully parsed.</summary> + [MemberNotNullWhen(true, nameof(ParsedLog.RawText))] public bool IsValid { get; set; } /// <summary>An error message indicating why the log file is invalid.</summary> - public string Error { get; set; } + public string? Error { get; set; } /// <summary>The raw log text.</summary> - public string RawText { get; set; } + public string? RawText { get; set; } /**** ** Log data ****/ /// <summary>The SMAPI version.</summary> - public string ApiVersion { get; set; } + public string? ApiVersion { get; set; } /// <summary>The game version.</summary> - public string GameVersion { get; set; } + public string? GameVersion { get; set; } /// <summary>The player's operating system.</summary> - public string OperatingSystem { get; set; } + public string? OperatingSystem { get; set; } /// <summary>The game install path.</summary> - public string GamePath { get; set; } + public string? GamePath { get; set; } /// <summary>The mod folder path.</summary> - public string ModPath { get; set; } + public string? ModPath { get; set; } /// <summary>The ISO 8601 timestamp when the log was started.</summary> public DateTimeOffset Timestamp { get; set; } @@ -47,6 +47,6 @@ namespace StardewModdingAPI.Web.Framework.LogParsing.Models public LogModInfo[] Mods { get; set; } = Array.Empty<LogModInfo>(); /// <summary>The log messages.</summary> - public LogMessage[] Messages { get; set; } + public LogMessage[] Messages { get; set; } = Array.Empty<LogMessage>(); } } diff --git a/src/SMAPI.Web/ViewModels/LogParserModel.cs b/src/SMAPI.Web/ViewModels/LogParserModel.cs index c768a08c..d7e4d810 100644 --- a/src/SMAPI.Web/ViewModels/LogParserModel.cs +++ b/src/SMAPI.Web/ViewModels/LogParserModel.cs @@ -1,9 +1,9 @@ -#nullable disable - using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text.RegularExpressions; +using Newtonsoft.Json; using StardewModdingAPI.Toolkit.Utilities; using StardewModdingAPI.Web.Framework.LogParsing.Models; @@ -23,40 +23,41 @@ namespace StardewModdingAPI.Web.ViewModels ** Accessors *********/ /// <summary>The paste ID.</summary> - public string PasteID { get; set; } + public string? PasteID { get; } /// <summary>The viewer's detected OS, if known.</summary> - public Platform? DetectedPlatform { get; set; } + public Platform? DetectedPlatform { get; } /// <summary>The parsed log info.</summary> - public ParsedLog ParsedLog { get; set; } + public ParsedLog? ParsedLog { get; private set; } /// <summary>Whether to show the raw unparsed log.</summary> - public bool ShowRaw { get; set; } + public bool ShowRaw { get; private set; } /// <summary>A non-blocking warning while uploading the log.</summary> - public string UploadWarning { get; set; } + public string? UploadWarning { get; set; } /// <summary>An error which occurred while uploading the log.</summary> - public string UploadError { get; set; } + public string? UploadError { get; set; } /// <summary>An error which occurred while parsing the log file.</summary> - public string ParseError => this.ParsedLog?.Error; + public string? ParseError => this.ParsedLog?.Error; /// <summary>When the uploaded file will no longer be available.</summary> public DateTime? Expiry { get; set; } + /// <summary>Whether parsed log data is available.</summary> + [MemberNotNullWhen(true, nameof(LogParserModel.PasteID), nameof(LogParserModel.ParsedLog))] + public bool HasLog => this.ParsedLog != null; + /********* ** Public methods *********/ /// <summary>Construct an instance.</summary> - public LogParserModel() { } - - /// <summary>Construct an instance.</summary> /// <param name="pasteID">The paste ID.</param> /// <param name="platform">The viewer's detected OS, if known.</param> - public LogParserModel(string pasteID, Platform? platform) + public LogParserModel(string? pasteID, Platform? platform) { this.PasteID = pasteID; this.DetectedPlatform = platform; @@ -64,6 +65,26 @@ namespace StardewModdingAPI.Web.ViewModels this.ShowRaw = false; } + /// <summary>Construct an instance.</summary> + /// <param name="pasteId">The paste ID.</param> + /// <param name="detectedPlatform">The viewer's detected OS, if known.</param> + /// <param name="parsedLog">The parsed log info.</param> + /// <param name="showRaw">Whether to show the raw unparsed log.</param> + /// <param name="uploadWarning">A non-blocking warning while uploading the log.</param> + /// <param name="uploadError">An error which occurred while uploading the log.</param> + /// <param name="expiry">When the uploaded file will no longer be available.</param> + [JsonConstructor] + public LogParserModel(string pasteId, Platform? detectedPlatform, ParsedLog? parsedLog, bool showRaw, string? uploadWarning, string? uploadError, DateTime? expiry) + { + this.PasteID = pasteId; + this.DetectedPlatform = detectedPlatform; + this.ParsedLog = parsedLog; + this.ShowRaw = showRaw; + this.UploadWarning = uploadWarning; + this.UploadError = uploadError; + this.Expiry = expiry; + } + /// <summary>Set the log parser result.</summary> /// <param name="parsedLog">The parsed log info.</param> /// <param name="showRaw">Whether to show the raw unparsed log.</param> @@ -79,14 +100,14 @@ namespace StardewModdingAPI.Web.ViewModels public IDictionary<string, LogModInfo[]> GetContentPacksByMod() { // get all mods & content packs - LogModInfo[] mods = this.ParsedLog?.Mods; + LogModInfo[]? mods = this.ParsedLog?.Mods; if (mods == null || !mods.Any()) return new Dictionary<string, LogModInfo[]>(); // group by mod return mods .Where(mod => mod.IsContentPack) - .GroupBy(mod => mod.ContentPackFor) + .GroupBy(mod => mod.ContentPackFor!) .ToDictionary(group => group.Key, group => group.ToArray()); } diff --git a/src/SMAPI.Web/Views/LogParser/Index.cshtml b/src/SMAPI.Web/Views/LogParser/Index.cshtml index cf7e0a5c..a7552888 100644 --- a/src/SMAPI.Web/Views/LogParser/Index.cshtml +++ b/src/SMAPI.Web/Views/LogParser/Index.cshtml @@ -1,7 +1,3 @@ -@{ - #nullable disable -} - @using Humanizer @using StardewModdingAPI.Toolkit.Utilities @using StardewModdingAPI.Web.Framework @@ -12,7 +8,7 @@ @{ ViewData["Title"] = "SMAPI log parser"; - ParsedLog log = Model!.ParsedLog; + ParsedLog? log = Model!.ParsedLog; IDictionary<string, LogModInfo[]> contentPacks = Model.GetContentPacksByMod(); IDictionary<string, bool> defaultFilters = Enum @@ -29,7 +25,7 @@ string curPageUrl = this.Url.PlainAction("Index", "LogParser", new { id = Model.PasteID }, absoluteUrl: true); - ISet<int> screenIds = new HashSet<int>(log?.Messages?.Select(p => p.ScreenId) ?? Array.Empty<int>()); + ISet<int> screenIds = new HashSet<int>(log?.Messages.Select(p => p.ScreenId) ?? Array.Empty<int>()); } @section Head { @@ -58,7 +54,7 @@ messages: @this.ForJson(log?.Messages), sections: @this.ForJson(logSections), logLevels: @this.ForJson(logLevels), - modSlugs: @this.ForJson(log?.Mods?.DistinctBy(p => p.Name).Select(p => new { p.Name, Slug = Model.GetSlug(p.Name) }).Where(p => p.Name != p.Slug).ToDictionary(p => p.Name, p => p.Slug)), + modSlugs: @this.ForJson(log?.Mods.DistinctBy(p => p.Name).Select(p => new { p.Name, Slug = Model.GetSlug(p.Name) }).Where(p => p.Name != p.Slug).ToDictionary(p => p.Name, p => p.Slug)), screenIds: @this.ForJson(screenIds) }, </text> @@ -68,7 +64,7 @@ <text>data: null,</text> } showPopup: @this.ForJson(log == null), - showMods: @this.ForJson(log?.Mods?.Select(p => Model.GetSlug(p.Name)).Distinct().ToDictionary(slug => slug, _ => true)), + showMods: @this.ForJson(log?.Mods.Select(p => Model.GetSlug(p.Name)).Distinct().ToDictionary(slug => slug, _ => true)), showSections: @this.ForJson(Enum.GetNames(typeof(LogSection)).ToDictionary(section => section, _ => false)), showLevels: @this.ForJson(defaultFilters), enableFilters: @this.ForJson(!Model.ShowRaw) @@ -76,7 +72,7 @@ "@this.Url.PlainAction("Index", "LogParser", values: null)" ); - new Tabby('[data-tabs]'); + new Tabby("[data-tabs]"); }); </script> } @@ -217,12 +213,12 @@ else if (log?.IsValid == true) Consider updating these mods to fix problems: <table id="updates" class="table"> - @foreach (LogModInfo mod in log.Mods.Where(mod => (mod.HasUpdate && !mod.IsContentPack) || (contentPacks.TryGetValue(mod.Name, out LogModInfo[] contentPackList) && contentPackList.Any(pack => pack.HasUpdate)))) + @foreach (LogModInfo mod in log.Mods.Where(mod => (mod.HasUpdate && !mod.IsContentPack) || (contentPacks.TryGetValue(mod.Name, out LogModInfo[]? contentPackList) && contentPackList.Any(pack => pack.HasUpdate)))) { <tr class="mod-entry"> <td> <strong class=@(!mod.HasUpdate ? "hidden" : "")>@mod.Name</strong> - @if (contentPacks != null && contentPacks.TryGetValue(mod.Name, out LogModInfo[] contentPackList)) + @if (contentPacks != null && contentPacks.TryGetValue(mod.Name, out LogModInfo[]? contentPackList)) { <div class="content-packs"> @foreach (LogModInfo contentPack in contentPackList.Where(pack => pack.HasUpdate)) @@ -305,8 +301,7 @@ else if (log?.IsValid == true) </caption> @foreach (var mod in log.Mods.Where(p => p.Loaded && !p.IsContentPack)) { - LogModInfo[]? contentPackList; - if (contentPacks == null || !contentPacks.TryGetValue(mod.Name, out contentPackList)) + if (contentPacks == null || !contentPacks.TryGetValue(mod.Name, out LogModInfo[]? contentPackList)) contentPackList = null; <tr v-on:click="toggleMod('@Model.GetSlug(mod.Name)')" class="mod-entry" v-bind:class="{ hidden: !showMods['@Model.GetSlug(mod.Name)'] }"> |