From 96040d2d8a9239a75d3e6cb41c83463c61bef0c1 Mon Sep 17 00:00:00 2001 From: Mira <56395159+TheXorog@users.noreply.github.com> Date: Tue, 30 May 2023 22:01:19 +0200 Subject: [PATCH] Update Scheduler, Add config for GetTimeFormat Config, Add Logger --- Global.cs | 4 +- Internal/Internal.cs | 137 +++++++++++++++++++++---------- Log.cs | 15 ++++ UniversalExtensions.cs | 118 +++++++++++++------------- Xorog.UniversalExtensions.csproj | 4 + 5 files changed, 174 insertions(+), 104 deletions(-) create mode 100644 Log.cs diff --git a/Global.cs b/Global.cs index 49783af..042e4fc 100644 --- a/Global.cs +++ b/Global.cs @@ -6,7 +6,9 @@ global using System.Collections.Generic; global using System.Text; global using System.Threading.Tasks; global using System.Drawing; +global using Microsoft.Extensions.Logging; global using static Xorog.UniversalExtensions.UniversalExtensionsEnums; global using static Xorog.UniversalExtensions.Internal; -global using static Xorog.UniversalExtensions.InternalSheduler; \ No newline at end of file +global using static Xorog.UniversalExtensions.InternalSheduler; +global using static Xorog.UniversalExtensions.Log; \ No newline at end of file diff --git a/Internal/Internal.cs b/Internal/Internal.cs index 5461c80..72bfb49 100644 --- a/Internal/Internal.cs +++ b/Internal/Internal.cs @@ -6,71 +6,64 @@ internal static class Internal { switch (timeFormat) { - case TimeFormat.HOURS: - if (_timespan.TotalDays >= 1) - return $"{(Math.Floor(_timespan.TotalHours).ToString().Length == 1 ? $"0{Math.Floor(_timespan.TotalHours)}" : Math.Floor(_timespan.TotalHours))}:" + - $"{(_timespan.Minutes.ToString().Length == 1 ? $"0{_timespan.Minutes}" : _timespan.Minutes)}:" + - $"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}"; - - if (_timespan.TotalHours >= 1) - return $"{(_timespan.Hours.ToString().Length == 1 ? $"0{_timespan.Hours}" : _timespan.Hours)}:" + - $"{(_timespan.Minutes.ToString().Length == 1 ? $"0{_timespan.Minutes}" : _timespan.Minutes)}:" + - $"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}"; - - return $"{(_timespan.Minutes.ToString().Length == 1 ? $"0{_timespan.Minutes}" : _timespan.Minutes)}:" + - $"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}"; case TimeFormat.DAYS: if (_timespan.TotalDays >= 1) - return $"{(Math.Floor(_timespan.TotalDays).ToString().Length == 1 ? $"0{Math.Floor(_timespan.TotalDays)}" : Math.Floor(_timespan.TotalDays))}" + - $"{(_timespan.Hours.ToString().Length == 1 ? $"0{_timespan.Hours}" : _timespan.Hours)}:" + - $"{(_timespan.Minutes.ToString().Length == 1 ? $"0{_timespan.Minutes}" : _timespan.Minutes)}:" + - $"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}"; + return $"{Math.Floor(_timespan.TotalDays).ToString().PadLeft(2, '0')}:{_timespan.Hours.ToString().PadLeft(2, '0')}:{_timespan.Minutes.ToString().PadLeft(2, '0')}:{_timespan.Seconds.ToString().PadLeft(2, '0')}"; if (_timespan.TotalHours >= 1) - return $"{(Math.Floor(_timespan.TotalHours).ToString().Length == 1 ? $"0{Math.Floor(_timespan.TotalHours)}" : Math.Floor(_timespan.TotalHours))}:" + - $"{(_timespan.Minutes.ToString().Length == 1 ? $"0{_timespan.Minutes}" : _timespan.Minutes)}:" + - $"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}"; + return $"{Math.Floor(_timespan.TotalHours).ToString().PadLeft(2, '0')}:{_timespan.Minutes.ToString().PadLeft(2, '0')}:{_timespan.Seconds.ToString().PadLeft(2, '0')}"; - return $"{(_timespan.Minutes.ToString().Length == 1 ? $"0{_timespan.Minutes}" : _timespan.Minutes)}:" + - $"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}"; + return $"{_timespan.Minutes.ToString().PadLeft(2, '0')}:{_timespan.Seconds.ToString().PadLeft(2, '0')}"; + + case TimeFormat.HOURS: + if (_timespan.TotalDays >= 1) + return $"{Math.Floor(_timespan.TotalHours).ToString().PadLeft(2, '0')}:" + + $"{_timespan.Minutes.ToString().PadLeft(2, '0')}:{_timespan.Seconds.ToString().PadLeft(2, '0')}"; + + if (_timespan.TotalHours >= 1) + return $"{_timespan.Hours.ToString().PadLeft(2, '0')}:" + + $"{_timespan.Minutes.ToString().PadLeft(2, '0')}:{_timespan.Seconds.ToString().PadLeft(2, '0')}"; + + return $"{_timespan.Minutes.ToString().PadLeft(2, '0')}:{_timespan.Seconds.ToString().PadLeft(2, '0')}"; case TimeFormat.MINUTES: if (_timespan.TotalHours >= 1) - return $"{(Math.Floor(_timespan.TotalMinutes).ToString().Length == 1 ? $"0{Math.Floor(_timespan.TotalMinutes)}" : Math.Floor(_timespan.TotalMinutes))}:" + - $"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}"; + return $"{Math.Floor(_timespan.TotalMinutes).ToString().PadLeft(2, '0')}:{_timespan.Seconds.ToString().PadLeft(2, '0')}"; - return $"{(_timespan.Minutes.ToString().Length == 1 ? $"0{_timespan.Minutes}" : _timespan.Minutes)}:" + - $"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}"; + return $"{_timespan.Minutes.ToString().PadLeft(2, '0')}:{_timespan.Seconds.ToString().PadLeft(2, '0')}"; default: return _timespan.ToString(); } } - internal static string GetTimeFormat(this TimeSpan _timespan, TimeFormat timeFormat) + internal static string GetTimeFormat(this TimeSpan _timespan, TimeFormat timeFormat, HumanReadableTimeFormatConfig? config = null) { + config ??= new(); + switch (timeFormat) { - case TimeFormat.HOURS: - if (_timespan.TotalDays >= 1) - return $"{Math.Floor(_timespan.TotalHours)} hours, {_timespan.Minutes} minutes"; - - if (_timespan.TotalHours >= 1) - return $"{_timespan.Hours} hours, {_timespan.Minutes} minutes"; - - return $"{_timespan.Minutes} minutes, {_timespan.Seconds} seconds"; case TimeFormat.DAYS: if (_timespan.TotalDays >= 1) - return $"{Math.Floor(_timespan.TotalDays)} days, {_timespan.Hours} hours"; + return $"{Math.Floor(_timespan.TotalDays)} {config.DaysString}, {_timespan.Hours} {config.HoursString}"; if (_timespan.TotalHours >= 1) - return $"{_timespan.Hours} hours, {_timespan.Minutes} minutes"; + return $"{_timespan.Hours} {config.HoursString}, {_timespan.Minutes} {config.MinutesString}"; - return $"{_timespan.Minutes} minutes, {_timespan.Seconds} seconds"; + return $"{_timespan.Minutes} {config.MinutesString}, {_timespan.Seconds} {config.SecondsString}"; + + case TimeFormat.HOURS: + if (_timespan.TotalDays >= 1) + return $"{Math.Floor(_timespan.TotalHours)} {config.HoursString}, {_timespan.Minutes} {config.MinutesString}"; + + if (_timespan.TotalHours >= 1) + return $"{_timespan.Hours} {config.HoursString}, {_timespan.Minutes} {config.MinutesString}"; + + return $"{_timespan.Minutes} {config.MinutesString}, {_timespan.Seconds} {config.SecondsString}"; case TimeFormat.MINUTES: if (_timespan.TotalHours >= 1) - return $"{Math.Floor(_timespan.TotalMinutes)} minutes, {_timespan.Seconds} seconds"; - return $"{_timespan.Minutes} minutes, {_timespan.Seconds} seconds"; + return $"{Math.Floor(_timespan.TotalMinutes)} {config.MinutesString}, {_timespan.Seconds} {config.SecondsString}"; + return $"{_timespan.Minutes} {config.MinutesString}, {_timespan.Seconds} {config.MinutesString}"; default: return _timespan.ToString(); @@ -88,12 +81,66 @@ internal static class Internal public class InternalSheduler { - public static Dictionary registeredScheduledTasks { get; internal set; } = new Dictionary(); + public static Dictionary RegisteredScheduledTasks { get; internal set; } = new Dictionary(); - public class taskInfo + public class ScheduledTask { - public string customId { get; internal set; } = ""; - internal CancellationTokenSource? tokenSource { get; set; } - public DateTime? runTime { get; internal set; } + public ScheduledTask() + { + this.Uid = Guid.NewGuid().ToString(); + } + + /// + /// The unique identifier of this task. + /// + public string Uid { get; internal set; } + + /// + /// The custom data asscociated with this task. + /// + public object CustomData { get; internal set; } + + /// + /// The time this task will run. + /// + public DateTime? RunTime { get; internal set; } + + /// + /// The to prematurely dequeue this task. + /// + internal CancellationTokenSource? TokenSource { get; set; } = new(); + + /// + /// Delete this task. + /// + public void Delete() => + UniversalExtensions.DeleteScheduledTask(Uid); } +} + +public class HumanReadableTimeFormatConfig +{ + /// + /// The string used for days. + /// Defaults to: "days". + /// + public string DaysString { get; set; } = "days"; + + /// + /// The string used for hours. + /// Defaults to: "hours". + /// + public string HoursString { get; set; } = "hours"; + + /// + /// The string used for minutes. + /// Defaults to: "minutes". + /// + public string MinutesString { get; set; } = "minutes"; + + /// + /// The string used for seconds. + /// Defaults to: "seconds". + /// + public string SecondsString { get; set; } = "seconds"; } \ No newline at end of file diff --git a/Log.cs b/Log.cs new file mode 100644 index 0000000..de3b543 --- /dev/null +++ b/Log.cs @@ -0,0 +1,15 @@ +// Project Makoto +// Copyright (C) 2023 Fortunevale +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY + +namespace Xorog.UniversalExtensions; + +internal class Log +{ + internal static ILogger? _logger { get; set; } +} diff --git a/UniversalExtensions.cs b/UniversalExtensions.cs index b59f75e..de7ddf4 100644 --- a/UniversalExtensions.cs +++ b/UniversalExtensions.cs @@ -2,6 +2,16 @@ public static class UniversalExtensions { + /// + /// Attaches a logger to UniversalExtensions. Used for Debug purposes. + /// + /// + public static void AttachLogger(ILogger logger) + { + _logger = logger; + } + + /// /// Extensions for string.IsNullOrWhiteSpace /// @@ -161,6 +171,8 @@ public static class UniversalExtensions /// The URL the redirect leads to public static async Task UnshortenUrl(string url, bool UseHeadMethod = true) { + _logger?.LogDebug("Unshortening Url '{Url}', using head method: {UseHeadMethod}", url, UseHeadMethod); + HttpClient client = new(new HttpClientHandler() { AllowAutoRedirect = false, @@ -194,14 +206,20 @@ public static class UniversalExtensions if (!request_task.IsCompleted) cancellationTokenSource.Cancel(); - if (UseHeadMethod && request_task.IsFaulted && request_task.Exception.InnerException.GetType().FullName == "System.Net.Http.HttpRequestException") + if (UseHeadMethod && request_task.IsFaulted && request_task.Exception.InnerException.GetType() == typeof(HttpRequestException)) + { + _logger?.LogWarning("Unshortening Url '{Url}' failed, falling back to non-head method", url); return await UnshortenUrl(url, false); + } var statuscode = request_task.Result.StatusCode; var header = request_task.Result.Headers; if (UseHeadMethod && statuscode is HttpStatusCode.NotFound or HttpStatusCode.InternalServerError) + { + _logger?.LogWarning("Unshortening Url '{Url}' failed, falling back to non-head method", url); return await UnshortenUrl(url, false); + } if (statuscode is HttpStatusCode.Found or HttpStatusCode.Redirect @@ -291,9 +309,10 @@ public static class UniversalExtensions /// /// The task to run /// The time to run the task + /// Any custom data you wish to provide. /// An unique identifier of the task - public static string CreateScheduleTask(this Task task, DateTime runTime, string CustomId = "") + public static string CreateScheduledTask(this Task task, DateTime runTime, object? customData = null) { string UID = Guid.NewGuid().ToString(); CancellationTokenSource CancellationToken = new CancellationTokenSource(); @@ -303,28 +322,28 @@ public static class UniversalExtensions _ = LongDelay(runTime.GetTimespanUntil(), CancellationToken).ContinueWith(x => { - if (registeredScheduledTasks.ContainsKey(UID)) - registeredScheduledTasks.Remove(UID); + lock (RegisteredScheduledTasks) + { + RegisteredScheduledTasks.Remove(UID); + } + + _logger?.LogDebug("Running scheduled task with UID '{UID}'", UID, runTime.GetTimespanUntil().GetHumanReadable()); if (x.IsCompletedSuccessfully) task.Start(); }); - if (registeredScheduledTasks is null) - registeredScheduledTasks = new(); + lock (RegisteredScheduledTasks) + { + _logger?.LogDebug("Creating scheduled task with UID '{UID}' running in {RunTime}", UID, runTime.GetTimespanUntil().GetHumanReadable()); - try - { - registeredScheduledTasks.Add(UID, new taskInfo { tokenSource = CancellationToken, customId = CustomId, runTime = runTime }); - } - catch (InvalidOperationException) - { - registeredScheduledTasks = new(); - registeredScheduledTasks.Add(UID, new taskInfo { tokenSource = CancellationToken, customId = CustomId, runTime = runTime }); - } - catch (Exception) - { - throw; + RegisteredScheduledTasks.Add(UID, new ScheduledTask + { + Uid = UID, + RunTime = runTime, + TokenSource = CancellationToken, + CustomData = customData, + }); } return UID; } @@ -335,17 +354,22 @@ public static class UniversalExtensions /// Deletes a scheduled task /// /// The task's unique identifier - /// Throws if the task hasn't been found or if an internal error occurred - public static void DeleteScheduleTask(string UID) + /// Throws if the task hasn't been found or if an internal error occurred + public static void DeleteScheduledTask(string UID) { - if (!registeredScheduledTasks.ContainsKey(UID)) - throw new Exception($"No scheduled task has been found with UID '{UID}'"); + if (!RegisteredScheduledTasks.ContainsKey(UID)) + throw new KeyNotFoundException($"No scheduled task has been found with UID '{UID}'"); - if (registeredScheduledTasks[ UID ].tokenSource is null) + if (RegisteredScheduledTasks[UID].TokenSource is null) throw new Exception($"Internal: There is no token source registered the specified task."); - registeredScheduledTasks[ UID ].tokenSource?.Cancel(); - registeredScheduledTasks.Remove(UID); + _logger?.LogDebug("Deleting scheduled task with UID '{UID}'", UID); + + lock (RegisteredScheduledTasks) + { + RegisteredScheduledTasks[UID].TokenSource?.Cancel(); + RegisteredScheduledTasks.Remove(UID); + } return; } @@ -355,15 +379,8 @@ public static class UniversalExtensions /// Gets a list of all registered tasks /// /// A list of all registered tasks - public static IReadOnlyDictionary? GetScheduleTasks() - { - if (registeredScheduledTasks is null) - registeredScheduledTasks = new(); - - return registeredScheduledTasks as IReadOnlyDictionary; - } - - + public static IReadOnlyList? GetScheduledTasks() + => RegisteredScheduledTasks.Select(x => x.Value).ToList().AsReadOnly(); /// /// Gets a specific task @@ -371,13 +388,8 @@ public static class UniversalExtensions /// The unique identifier of what task to get /// The task /// Throws if the task has not been found - public static taskInfo GetScheduleTask(string UID) - { - if (!registeredScheduledTasks.ContainsKey(UID)) - throw new Exception($"The specified task doesn't exist."); - - return registeredScheduledTasks[UID]; - } + public static ScheduledTask GetScheduledTask(string UID) + => RegisteredScheduledTasks[UID]; @@ -513,33 +525,23 @@ public static class UniversalExtensions /// - /// Get a human readable string for the given amount of seconds + /// Get a human readable string for the given amount of time. /// /// /// /// - public static string GetHumanReadable(this int seconds, TimeFormat timeFormat = TimeFormat.DAYS) => - TimeSpan.FromSeconds(seconds).GetTimeFormat(timeFormat); + public static string GetHumanReadable(this int seconds, TimeFormat timeFormat = TimeFormat.DAYS, HumanReadableTimeFormatConfig? config = null) => + TimeSpan.FromSeconds(seconds).GetTimeFormat(timeFormat,config); - /// /// - /// - /// - /// - /// - public static string GetHumanReadable(this long seconds, TimeFormat timeFormat = TimeFormat.DAYS) => - TimeSpan.FromSeconds(seconds).GetTimeFormat(timeFormat); + public static string GetHumanReadable(this long seconds, TimeFormat timeFormat = TimeFormat.DAYS, HumanReadableTimeFormatConfig? config = null) => + TimeSpan.FromSeconds(seconds).GetTimeFormat(timeFormat, config); - /// /// - /// - /// - /// - /// - public static string GetHumanReadable(this TimeSpan timeSpan, TimeFormat timeFormat = TimeFormat.DAYS) => - timeSpan.GetTimeFormat(timeFormat); + public static string GetHumanReadable(this TimeSpan timeSpan, TimeFormat timeFormat = TimeFormat.DAYS, HumanReadableTimeFormatConfig? config = null) => + timeSpan.GetTimeFormat(timeFormat, config); diff --git a/Xorog.UniversalExtensions.csproj b/Xorog.UniversalExtensions.csproj index 1ba4264..fe24221 100644 --- a/Xorog.UniversalExtensions.csproj +++ b/Xorog.UniversalExtensions.csproj @@ -36,4 +36,8 @@ + + + +