Added negative TimeSpan handling (#1666)
- Added unit tests for the TimeSpanTypeReader - Fixes https://github.com/discord-net/Discord.Net/issues/1657
This commit is contained in:
@@ -6,15 +6,19 @@ namespace Discord.Commands
|
|||||||
{
|
{
|
||||||
internal class TimeSpanTypeReader : TypeReader
|
internal class TimeSpanTypeReader : TypeReader
|
||||||
{
|
{
|
||||||
private static readonly string[] Formats = {
|
/// <summary>
|
||||||
"%d'd'%h'h'%m'm'%s's'", //4d3h2m1s
|
/// TimeSpan try parse formats.
|
||||||
"%d'd'%h'h'%m'm'", //4d3h2m
|
/// </summary>
|
||||||
"%d'd'%h'h'%s's'", //4d3h 1s
|
private static readonly string[] Formats =
|
||||||
"%d'd'%h'h'", //4d3h
|
{
|
||||||
"%d'd'%m'm'%s's'", //4d 2m1s
|
"%d'd'%h'h'%m'm'%s's'", // 4d3h2m1s
|
||||||
"%d'd'%m'm'", //4d 2m
|
"%d'd'%h'h'%m'm'", // 4d3h2m
|
||||||
"%d'd'%s's'", //4d 1s
|
"%d'd'%h'h'%s's'", // 4d3h 1s
|
||||||
"%d'd'", //4d
|
"%d'd'%h'h'", // 4d3h
|
||||||
|
"%d'd'%m'm'%s's'", // 4d 2m1s
|
||||||
|
"%d'd'%m'm'", // 4d 2m
|
||||||
|
"%d'd'%s's'", // 4d 1s
|
||||||
|
"%d'd'", // 4d
|
||||||
"%h'h'%m'm'%s's'", // 3h2m1s
|
"%h'h'%m'm'%s's'", // 3h2m1s
|
||||||
"%h'h'%m'm'", // 3h2m
|
"%h'h'%m'm'", // 3h2m
|
||||||
"%h'h'%s's'", // 3h 1s
|
"%h'h'%s's'", // 3h 1s
|
||||||
@@ -27,9 +31,25 @@ namespace Discord.Commands
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input, IServiceProvider services)
|
public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input, IServiceProvider services)
|
||||||
{
|
{
|
||||||
return (TimeSpan.TryParseExact(input.ToLowerInvariant(), Formats, CultureInfo.InvariantCulture, out var timeSpan))
|
if (string.IsNullOrEmpty(input))
|
||||||
? Task.FromResult(TypeReaderResult.FromSuccess(timeSpan))
|
throw new ArgumentException(message: $"{nameof(input)} must not be null or empty.", paramName: nameof(input));
|
||||||
: Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse TimeSpan"));
|
|
||||||
|
var isNegative = input[0] == '-'; // Char for CultureInfo.InvariantCulture.NumberFormat.NegativeSign
|
||||||
|
if (isNegative)
|
||||||
|
{
|
||||||
|
input = input.Substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TimeSpan.TryParseExact(input.ToLowerInvariant(), Formats, CultureInfo.InvariantCulture, out var timeSpan))
|
||||||
|
{
|
||||||
|
return isNegative
|
||||||
|
? Task.FromResult(TypeReaderResult.FromSuccess(-timeSpan))
|
||||||
|
: Task.FromResult(TypeReaderResult.FromSuccess(timeSpan));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse TimeSpan"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
70
test/Discord.Net.Tests.Unit/TimeSpanTypeReaderTests.cs
Normal file
70
test/Discord.Net.Tests.Unit/TimeSpanTypeReaderTests.cs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
using Discord.Commands;
|
||||||
|
using System;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Discord
|
||||||
|
{
|
||||||
|
public class TimeSpanTypeReaderTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData("4d3h2m1s", false)] // tests format "%d'd'%h'h'%m'm'%s's'"
|
||||||
|
[InlineData("4d3h2m", false)] // tests format "%d'd'%h'h'%m'm'"
|
||||||
|
[InlineData("4d3h1s", false)] // tests format "%d'd'%h'h'%s's'"
|
||||||
|
[InlineData("4d3h", false)] // tests format "%d'd'%h'h'"
|
||||||
|
[InlineData("4d2m1s", false)] // tests format "%d'd'%m'm'%s's'"
|
||||||
|
[InlineData("4d2m", false)] // tests format "%d'd'%m'm'"
|
||||||
|
[InlineData("4d1s", false)] // tests format "%d'd'%s's'"
|
||||||
|
[InlineData("4d", false)] // tests format "%d'd'"
|
||||||
|
[InlineData("3h2m1s", false)] // tests format "%h'h'%m'm'%s's'"
|
||||||
|
[InlineData("3h2m", false)] // tests format "%h'h'%m'm'"
|
||||||
|
[InlineData("3h1s", false)] // tests format "%h'h'%s's'"
|
||||||
|
[InlineData("3h", false)] // tests format "%h'h'"
|
||||||
|
[InlineData("2m1s", false)] // tests format "%m'm'%s's'"
|
||||||
|
[InlineData("2m", false)] // tests format "%m'm'"
|
||||||
|
[InlineData("1s", false)] // tests format "%s's'"
|
||||||
|
// Negatives
|
||||||
|
[InlineData("-4d3h2m1s", true)] // tests format "-%d'd'%h'h'%m'm'%s's'"
|
||||||
|
[InlineData("-4d3h2m", true)] // tests format "-%d'd'%h'h'%m'm'"
|
||||||
|
[InlineData("-4d3h1s", true)] // tests format "-%d'd'%h'h'%s's'"
|
||||||
|
[InlineData("-4d3h", true)] // tests format "-%d'd'%h'h'"
|
||||||
|
[InlineData("-4d2m1s", true)] // tests format "-%d'd'%m'm'%s's'"
|
||||||
|
[InlineData("-4d2m", true)] // tests format "-%d'd'%m'm'"
|
||||||
|
[InlineData("-4d1s", true)] // tests format "-%d'd'%s's'"
|
||||||
|
[InlineData("-4d", true)] // tests format "-%d'd'"
|
||||||
|
[InlineData("-3h2m1s", true)] // tests format "-%h'h'%m'm'%s's'"
|
||||||
|
[InlineData("-3h2m", true)] // tests format "-%h'h'%m'm'"
|
||||||
|
[InlineData("-3h1s", true)] // tests format "-%h'h'%s's'"
|
||||||
|
[InlineData("-3h", true)] // tests format "-%h'h'"
|
||||||
|
[InlineData("-2m1s", true)] // tests format "-%m'm'%s's'"
|
||||||
|
[InlineData("-2m", true)] // tests format "-%m'm'"
|
||||||
|
[InlineData("-1s", true)] // tests format "-%s's'"
|
||||||
|
public void TestTimeSpanParse(string input, bool isNegative)
|
||||||
|
{
|
||||||
|
var reader = new TimeSpanTypeReader();
|
||||||
|
var result = reader.ReadAsync(null, input, null).Result;
|
||||||
|
Assert.True(result.IsSuccess);
|
||||||
|
|
||||||
|
var actual = (TimeSpan)result.BestMatch;
|
||||||
|
Assert.True(actual != TimeSpan.Zero);
|
||||||
|
|
||||||
|
if (isNegative)
|
||||||
|
{
|
||||||
|
Assert.True(actual < TimeSpan.Zero);
|
||||||
|
|
||||||
|
Assert.True(actual.Seconds == 0 || actual.Seconds == -1);
|
||||||
|
Assert.True(actual.Minutes == 0 || actual.Minutes == -2);
|
||||||
|
Assert.True(actual.Hours == 0 || actual.Hours == -3);
|
||||||
|
Assert.True(actual.Days == 0 || actual.Days == -4);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.True(actual > TimeSpan.Zero);
|
||||||
|
|
||||||
|
Assert.True(actual.Seconds == 0 || actual.Seconds == 1);
|
||||||
|
Assert.True(actual.Minutes == 0 || actual.Minutes == 2);
|
||||||
|
Assert.True(actual.Hours == 0 || actual.Hours == 3);
|
||||||
|
Assert.True(actual.Days == 0 || actual.Days == 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user