From f9aa76e41f84ff9ec70dc9bf5178f50617e45424 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 6 Nov 2016 10:30:25 -0500 Subject: use more nuanced deprecation warnings (#165) --- src/StardewModdingAPI/Config.cs | 3 +- src/StardewModdingAPI/Extensions.cs | 33 ++++++++--------- .../Framework/DeprecationLevel.cs | 15 ++++++++ .../Framework/DeprecationManager.cs | 41 ++++++++++++++++++---- src/StardewModdingAPI/Framework/LogWriter.cs | 9 +++-- src/StardewModdingAPI/Log.cs | 14 ++++++++ src/StardewModdingAPI/LogInfo.cs | 4 +++ src/StardewModdingAPI/Mod.cs | 9 ++--- src/StardewModdingAPI/Program.cs | 6 ++-- src/StardewModdingAPI/StardewModdingAPI.csproj | 1 + src/StardewModdingAPI/Version.cs | 3 +- 11 files changed, 103 insertions(+), 35 deletions(-) create mode 100644 src/StardewModdingAPI/Framework/DeprecationLevel.cs (limited to 'src') diff --git a/src/StardewModdingAPI/Config.cs b/src/StardewModdingAPI/Config.cs index 91503a83..326685c1 100644 --- a/src/StardewModdingAPI/Config.cs +++ b/src/StardewModdingAPI/Config.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using StardewModdingAPI.Framework; namespace StardewModdingAPI { @@ -107,7 +108,7 @@ namespace StardewModdingAPI /// Construct an instance. protected Config() { - Program.DeprecationManager.Warn("the Config class", "1.0"); + Program.DeprecationManager.Warn("the Config class", "1.0", DeprecationLevel.Notice); Program.DeprecationManager.MarkWarned($"{nameof(Mod)}.{nameof(Mod.BaseConfigPath)}", "1.0"); // typically used to construct config, avoid redundant warnings } } diff --git a/src/StardewModdingAPI/Extensions.cs b/src/StardewModdingAPI/Extensions.cs index d76523cd..a14ca613 100644 --- a/src/StardewModdingAPI/Extensions.cs +++ b/src/StardewModdingAPI/Extensions.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Reflection; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; +using StardewModdingAPI.Framework; namespace StardewModdingAPI { @@ -26,7 +27,7 @@ namespace StardewModdingAPI { get { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.Random)}", "1.0"); + Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.Random)}", "1.0", DeprecationLevel.Info); return Extensions._random; } } @@ -39,7 +40,7 @@ namespace StardewModdingAPI /// The key to check. public static bool IsKeyDown(this Keys key) { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.IsKeyDown)}", "1.0"); + Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.IsKeyDown)}", "1.0", DeprecationLevel.Info); return Keyboard.GetState().IsKeyDown(key); } @@ -47,7 +48,7 @@ namespace StardewModdingAPI /// Get a random color. public static Color RandomColour() { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.RandomColour)}", "1.0"); + Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.RandomColour)}", "1.0", DeprecationLevel.Info); return new Color(Extensions.Random.Next(0, 255), Extensions.Random.Next(0, 255), Extensions.Random.Next(0, 255)); } @@ -58,7 +59,7 @@ namespace StardewModdingAPI [Obsolete("The usage of ToSingular has changed. Please update your call to use ToSingular")] public static string ToSingular(this IEnumerable ienum, string split = ", ") { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.ToSingular)}", "1.0"); + Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.ToSingular)}", "1.0", DeprecationLevel.Info); Log.Error("The usage of ToSingular has changed. Please update your call to use ToSingular"); return ""; } @@ -69,7 +70,7 @@ namespace StardewModdingAPI /// The value separator. public static string ToSingular(this IEnumerable ienum, string split = ", ") { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.ToSingular)}", "1.0"); + Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.ToSingular)}", "1.0", DeprecationLevel.Info); //Apparently Keys[] won't split normally :l if (typeof(T) == typeof(Keys)) @@ -83,7 +84,7 @@ namespace StardewModdingAPI /// The value. public static bool IsInt32(this object o) { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.IsInt32)}", "1.0"); + Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.IsInt32)}", "1.0", DeprecationLevel.Info); int i; return int.TryParse(o.ToString(), out i); @@ -93,7 +94,7 @@ namespace StardewModdingAPI /// The value. public static int AsInt32(this object o) { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.AsInt32)}", "1.0"); + Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.AsInt32)}", "1.0", DeprecationLevel.Info); return int.Parse(o.ToString()); } @@ -102,7 +103,7 @@ namespace StardewModdingAPI /// The value. public static bool IsBool(this object o) { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.IsBool)}", "1.0"); + Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.IsBool)}", "1.0", DeprecationLevel.Info); bool b; return bool.TryParse(o.ToString(), out b); @@ -112,7 +113,7 @@ namespace StardewModdingAPI /// The value. public static bool AsBool(this object o) { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.AsBool)}", "1.0"); + Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.AsBool)}", "1.0", DeprecationLevel.Info); return bool.Parse(o.ToString()); } @@ -121,7 +122,7 @@ namespace StardewModdingAPI /// The values to hash. public static int GetHash(this IEnumerable enumerable) { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.GetHash)}", "1.0"); + Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.GetHash)}", "1.0", DeprecationLevel.Info); var hash = 0; foreach (var v in enumerable) @@ -134,7 +135,7 @@ namespace StardewModdingAPI /// The value. public static T Cast(this object o) where T : class { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.Cast)}", "1.0"); + Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.Cast)}", "1.0", DeprecationLevel.Info); return o as T; } @@ -143,7 +144,7 @@ namespace StardewModdingAPI /// The object to scan. public static FieldInfo[] GetPrivateFields(this object o) { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.GetPrivateFields)}", "1.0"); + Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.GetPrivateFields)}", "1.0", DeprecationLevel.Info); return o.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static); } @@ -152,7 +153,7 @@ namespace StardewModdingAPI /// The name of the field to find. public static FieldInfo GetBaseFieldInfo(this Type t, string name) { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.GetBaseFieldValue)}", "1.0"); + Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.GetBaseFieldValue)}", "1.0", DeprecationLevel.Info); return t.GetField(name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static); } @@ -162,7 +163,7 @@ namespace StardewModdingAPI /// The name of the field to find. public static T GetBaseFieldValue(this Type t, object o, string name) where T : class { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.GetBaseFieldValue)}", "1.0"); + Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.GetBaseFieldValue)}", "1.0", DeprecationLevel.Info); return t.GetBaseFieldInfo(name).GetValue(o) as T; } @@ -173,7 +174,7 @@ namespace StardewModdingAPI /// The value to set. public static void SetBaseFieldValue(this Type t, object o, string name, object newValue) where T : class { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.SetBaseFieldValue)}", "1.0"); + Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.SetBaseFieldValue)}", "1.0", DeprecationLevel.Info); t.GetBaseFieldInfo(name).SetValue(o, newValue as T); } @@ -181,7 +182,7 @@ namespace StardewModdingAPI /// The string to copy. public static string RemoveNumerics(this string st) { - Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.RemoveNumerics)}", "1.0"); + Program.DeprecationManager.Warn($"{nameof(Extensions)}.{nameof(Extensions.RemoveNumerics)}", "1.0", DeprecationLevel.Info); var s = st; foreach (var c in s) { diff --git a/src/StardewModdingAPI/Framework/DeprecationLevel.cs b/src/StardewModdingAPI/Framework/DeprecationLevel.cs new file mode 100644 index 00000000..c0044053 --- /dev/null +++ b/src/StardewModdingAPI/Framework/DeprecationLevel.cs @@ -0,0 +1,15 @@ +namespace StardewModdingAPI.Framework +{ + /// Indicates how deprecated something is. + internal enum DeprecationLevel + { + /// It's deprecated but won't be removed soon. Mod authors have some time to update their mods. Deprecation warnings should be logged, but not written to the console. + Notice, + + /// Mods should no longer be using it. Deprecation messages should be debug entries in the console. + Info, + + /// The code will be removed soon. Deprecation messages should be warnings in the console. + PendingRemoval + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Framework/DeprecationManager.cs b/src/StardewModdingAPI/Framework/DeprecationManager.cs index 2d4ff614..a3d1ea41 100644 --- a/src/StardewModdingAPI/Framework/DeprecationManager.cs +++ b/src/StardewModdingAPI/Framework/DeprecationManager.cs @@ -32,24 +32,51 @@ namespace StardewModdingAPI.Framework /// Log a deprecation warning. /// A noun phrase describing what is deprecated. /// The SMAPI version which deprecated it. - public void Warn(string nounPhrase, string version) + /// How deprecated the code is. + public void Warn(string nounPhrase, string version, DeprecationLevel severity) { - this.Warn(this.GetSourceNameFromStack(), nounPhrase, version); + this.Warn(this.GetSourceNameFromStack(), nounPhrase, version, severity); } /// Log a deprecation warning. /// The friendly mod name which used the deprecated code. /// A noun phrase describing what is deprecated. /// The SMAPI version which deprecated it. - public void Warn(string source, string nounPhrase, string version) + /// How deprecated the code is. + public void Warn(string source, string nounPhrase, string version, DeprecationLevel severity) { + // ignore if already warned if (source != null && !this.MarkWarned(source, nounPhrase, version)) return; - Log.Debug(source != null - ? $"NOTE: {source} used {nounPhrase}, which is deprecated since SMAPI {version}. It will work fine for now, but may be removed in a future version of SMAPI." - : $"NOTE: an unknown mod used {nounPhrase}, which is deprecated since SMAPI {version}. It will work fine for now, but may be removed in a future version of SMAPI.\n{Environment.StackTrace}" - ); + // build message + string message = source != null + ? $"{source} used {nounPhrase}, which is deprecated since SMAPI {version}." + : $"An unknown mod used {nounPhrase}, which is deprecated since SMAPI {version}."; + message += severity != DeprecationLevel.PendingRemoval + ? " It will work fine for now, but may be removed in a future version of SMAPI." + : " It will be removed soon, so the mod will break if it's not updated."; + if (source == null) + message += $"{Environment.NewLine}{Environment.StackTrace}"; + + // log message + switch (severity) + { + case DeprecationLevel.Notice: + Log.LogToFile(message); + break; + + case DeprecationLevel.Info: + Log.Debug(message); + break; + + case DeprecationLevel.PendingRemoval: + Log.Warning(message); + break; + + default: + throw new NotImplementedException($"Unknown deprecation level '{severity}'"); + } } /// Mark a deprecation warning as already logged. diff --git a/src/StardewModdingAPI/Framework/LogWriter.cs b/src/StardewModdingAPI/Framework/LogWriter.cs index 64769d2e..c21d53f4 100644 --- a/src/StardewModdingAPI/Framework/LogWriter.cs +++ b/src/StardewModdingAPI/Framework/LogWriter.cs @@ -74,9 +74,12 @@ namespace StardewModdingAPI.Framework { string message = $"[{entry.LogTime}] {entry.Message}"; - Console.ForegroundColor = entry.Colour; - Console.WriteLine(message); - Console.ForegroundColor = ConsoleColor.Gray; + if (entry.PrintConsole) + { + Console.ForegroundColor = entry.Colour; + Console.WriteLine(message); + Console.ForegroundColor = ConsoleColor.Gray; + } this.FileStream.WriteLine(message); } diff --git a/src/StardewModdingAPI/Log.cs b/src/StardewModdingAPI/Log.cs index 8923e10f..1ac20040 100644 --- a/src/StardewModdingAPI/Log.cs +++ b/src/StardewModdingAPI/Log.cs @@ -114,6 +114,13 @@ namespace StardewModdingAPI Log.AsyncColour(message?.ToString(), ConsoleColor.Magenta); } + /// Asynchronously log a warning to the console. + /// The message to log. + public static void Warning(object message) + { + Log.AsyncY("[WARN] " + message); + } + /// Asynchronously log an error to the console. /// The message to log. public static void Error(object message) @@ -142,6 +149,13 @@ namespace StardewModdingAPI Log.AsyncColour(message, ConsoleColor.DarkGray); } + /// Asynchronously log a message to the file that's not shown in the console. + /// The message to log. + internal static void LogToFile(string message) + { + Task.Run(() => { Log.PrintLog(new LogInfo(message) { PrintConsole = false }); }); + } + /********* ** Private methods *********/ diff --git a/src/StardewModdingAPI/LogInfo.cs b/src/StardewModdingAPI/LogInfo.cs index 2e8d42b6..ffef7cef 100644 --- a/src/StardewModdingAPI/LogInfo.cs +++ b/src/StardewModdingAPI/LogInfo.cs @@ -20,6 +20,9 @@ namespace StardewModdingAPI /// The message color. public ConsoleColor Colour { get; set; } + /// Whether the message should be printed to the console. + internal bool PrintConsole { get; set; } + /********* ** Public methods @@ -35,6 +38,7 @@ namespace StardewModdingAPI this.LogDate = DateTime.Now.ToString("yyyy-MM-dd"); this.LogTime = DateTime.Now.ToString("HH:mm:ss"); this.Colour = color; + this.PrintConsole = true; } } } diff --git a/src/StardewModdingAPI/Mod.cs b/src/StardewModdingAPI/Mod.cs index fa70d291..429ab046 100644 --- a/src/StardewModdingAPI/Mod.cs +++ b/src/StardewModdingAPI/Mod.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using StardewModdingAPI.Framework; namespace StardewModdingAPI { @@ -27,7 +28,7 @@ namespace StardewModdingAPI { get { - Program.DeprecationManager.Warn($"{nameof(Mod)}.{nameof(PathOnDisk)}", "1.0"); + Program.DeprecationManager.Warn($"{nameof(Mod)}.{nameof(PathOnDisk)}", "1.0", DeprecationLevel.Notice); return this._pathOnDisk; } internal set { this._pathOnDisk = value; } @@ -39,7 +40,7 @@ namespace StardewModdingAPI { get { - Program.DeprecationManager.Warn($"{nameof(Mod)}.{nameof(BaseConfigPath)}", "1.0"); + Program.DeprecationManager.Warn($"{nameof(Mod)}.{nameof(BaseConfigPath)}", "1.0", DeprecationLevel.Notice); Program.DeprecationManager.MarkWarned($"{nameof(Mod)}.{nameof(PathOnDisk)}", "1.0"); // avoid redundant warnings return Path.Combine(this.PathOnDisk, "config.json"); } @@ -55,7 +56,7 @@ namespace StardewModdingAPI { get { - Program.DeprecationManager.Warn($"{nameof(Mod)}.{nameof(PerSaveConfigPath)}", "1.0"); + Program.DeprecationManager.Warn($"{nameof(Mod)}.{nameof(PerSaveConfigPath)}", "1.0", DeprecationLevel.Notice); Program.DeprecationManager.MarkWarned($"{nameof(Mod)}.{nameof(PerSaveConfigFolder)}", "1.0"); // avoid redundant warnings return Constants.CurrentSavePathExists ? Path.Combine(this.PerSaveConfigFolder, Constants.SaveFolderName + ".json") : ""; } @@ -80,7 +81,7 @@ namespace StardewModdingAPI /// Get the full path to the per-save configuration file for the current save (if is true). private string GetPerSaveConfigFolder() { - Program.DeprecationManager.Warn($"{nameof(Mod)}.{nameof(PerSaveConfigFolder)}", "1.0"); + Program.DeprecationManager.Warn($"{nameof(Mod)}.{nameof(PerSaveConfigFolder)}", "1.0", DeprecationLevel.Notice); Program.DeprecationManager.MarkWarned($"{nameof(Mod)}.{nameof(PathOnDisk)}", "1.0"); // avoid redundant warnings if (!this.Manifest.PerSaveConfigs) diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index 3831e3d4..d44dc81b 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -272,7 +272,7 @@ namespace StardewModdingAPI // log deprecated fields if(manifest.UsedAuthourField) - Program.DeprecationManager.Warn(manifest.Name, $"{nameof(Manifest)}.{nameof(Manifest.Authour)}", "1.0"); + Program.DeprecationManager.Warn(manifest.Name, $"{nameof(Manifest)}.{nameof(Manifest.Authour)}", "1.0", DeprecationLevel.Notice); } catch (Exception ex) { @@ -283,7 +283,7 @@ namespace StardewModdingAPI // create per-save directory if (manifest.PerSaveConfigs) { - Program.DeprecationManager.Warn($"{nameof(Manifest)}.{nameof(Manifest.PerSaveConfigs)}", "1.0"); + Program.DeprecationManager.Warn($"{nameof(Manifest)}.{nameof(Manifest.PerSaveConfigs)}", "1.0", DeprecationLevel.Notice); try { string psDir = Path.Combine(directory, "psconfigs"); @@ -333,7 +333,7 @@ namespace StardewModdingAPI // raise deprecation warning for old Entry() method if (Program.DeprecationManager.IsVirtualMethodImplemented(modEntryType, typeof(Mod), nameof(Mod.Entry))) - Program.DeprecationManager.Warn(manifest.Name, $"an old version of {nameof(Mod)}.{nameof(Mod.Entry)}", "1.0"); + Program.DeprecationManager.Warn(manifest.Name, $"an old version of {nameof(Mod)}.{nameof(Mod.Entry)}", "1.0", DeprecationLevel.Notice); } } else diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index 5a901e4b..5540267a 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -196,6 +196,7 @@ + diff --git a/src/StardewModdingAPI/Version.cs b/src/StardewModdingAPI/Version.cs index 75195820..3eacb2ae 100644 --- a/src/StardewModdingAPI/Version.cs +++ b/src/StardewModdingAPI/Version.cs @@ -1,6 +1,7 @@ using System; using System.Text.RegularExpressions; using Newtonsoft.Json; +using StardewModdingAPI.Framework; namespace StardewModdingAPI { @@ -37,7 +38,7 @@ namespace StardewModdingAPI { get { - Program.DeprecationManager.Warn($"{nameof(Version)}.{nameof(Version.VersionString)}", "1.0"); + Program.DeprecationManager.Warn($"{nameof(Version)}.{nameof(Version.VersionString)}", "1.0", DeprecationLevel.Notice); return this.ToString(); } } -- cgit