From 270e16a3cb20ad68e819a74dc72d7ca486cea713 Mon Sep 17 00:00:00 2001 From: mctaylors Date: Thu, 28 Dec 2023 09:56:52 +0300 Subject: [PATCH] fully functional TimeSpanParser Signed-off-by: mctaylors --- src/Parsers/TimeSpanParser.cs | 97 ++++++++++++++++++++++++++++++++--- 1 file changed, 91 insertions(+), 6 deletions(-) diff --git a/src/Parsers/TimeSpanParser.cs b/src/Parsers/TimeSpanParser.cs index ef5dd1c..a8d2aa5 100644 --- a/src/Parsers/TimeSpanParser.cs +++ b/src/Parsers/TimeSpanParser.cs @@ -1,15 +1,25 @@ -using JetBrains.Annotations; +using System.Globalization; +using System.Text.RegularExpressions; +using JetBrains.Annotations; using Remora.Commands.Parsers; using Remora.Results; namespace Octobot.Parsers; /// -/// Parses . +/// Parses from . /// +/// +/// Parsed . +/// +/// +/// If parse wasn't successful, will return . +/// [PublicAPI] -public class TimeSpanParser : AbstractTypeParser +public partial class TimeSpanParser : AbstractTypeParser { + private static readonly Regex Pattern = ParseRegex(); + public static Result TryParse(string timeSpanString, CancellationToken ct = default) { if (timeSpanString.StartsWith('-')) @@ -17,8 +27,83 @@ public class TimeSpanParser : AbstractTypeParser return TimeSpan.Zero; } - var parser = new Remora.Commands.Parsers.TimeSpanParser(); - var parseResult = parser.TryParseAsync(timeSpanString, ct).AsTask().GetAwaiter().GetResult(); - return !parseResult.IsDefined(out var @in) ? TimeSpan.Zero : @in; + timeSpanString = timeSpanString.Trim(); + + if (TimeSpan.TryParse(timeSpanString, DateTimeFormatInfo.InvariantInfo, out var parsedTimeSpan)) + { + return parsedTimeSpan; + } + + var matches = ParseRegex().Matches(timeSpanString); + if (matches.Count is 0) + { + return TimeSpan.Zero; + } + + var timeSpan = TimeSpan.Zero; + foreach (var groups in matches.Select(match => match.Groups + .Cast() + .Where(g => g.Success) + .Skip(1) + .Select(g => (g.Name, g.Value)))) + { + foreach ((var key, var groupValue) in groups) + { + return !double.TryParse(groupValue, out var parsedGroupValue) + ? TimeSpan.Zero + : ParseFromRegex(timeSpan, key, groupValue, parsedGroupValue); + } + } + + return timeSpan; } + + private static Result ParseFromRegex(TimeSpan timeSpan, + string key, string groupValue, double parsedGroupValue) + { + if (key is "Years" or "Months") + { + if (!int.TryParse(groupValue, out var parsedIntegerValue)) + { + return TimeSpan.Zero; + } + + switch (key) + { + case "Years": + { + var now = DateTimeOffset.UtcNow; + var then = now.AddYears(parsedIntegerValue); + + timeSpan += then - now; + break; + } + case "Months": + { + var now = DateTimeOffset.UtcNow; + var then = now.AddMonths(parsedIntegerValue); + + timeSpan += then - now; + break; + } + } + + return timeSpan; + } + + timeSpan += key switch + { + "Weeks" => TimeSpan.FromDays(parsedGroupValue * 7), + "Days" => TimeSpan.FromDays(parsedGroupValue), + "Hours" => TimeSpan.FromHours(parsedGroupValue), + "Minutes" => TimeSpan.FromMinutes(parsedGroupValue), + "Seconds" => TimeSpan.FromSeconds(parsedGroupValue), + _ => throw new ArgumentOutOfRangeException(key) + }; + + return timeSpan; + } + + [GeneratedRegex("(?\\d+(?=y|л|г))|(?\\d+(?=mo|мес))|(?\\d+(?=w|н|нед))|(?\\d+(?=d|дн))|(?\\d+(?=h|ч))|(?\\d+(?=m|min|мин|м))|(?\\d+(?=s|sec|с|сек))")] + private static partial Regex ParseRegex(); }