enable nullable annotations in log parser (#837)
This commit is contained in:
parent
ad4d065fe7
commit
650af7ef1a
|
@ -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();
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System;
|
||||
|
||||
namespace StardewModdingAPI.Web.Framework.LogParsing
|
||||
|
|
|
@ -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()!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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)'] }">
|
||||
|
|
Loading…
Reference in New Issue