mirror of
https://github.com/ppy/osu
synced 2025-01-20 13:00:54 +00:00
Allow more lenient parsing of incoming timestamps
This commit is contained in:
parent
e1827ac28d
commit
44b9a06639
43
osu.Game.Tests/Editing/EditorTimestampParserTest.cs
Normal file
43
osu.Game.Tests/Editing/EditorTimestampParserTest.cs
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
|
||||
namespace osu.Game.Tests.Editing
|
||||
{
|
||||
[TestFixture]
|
||||
public class EditorTimestampParserTest
|
||||
{
|
||||
public static readonly object?[][] test_cases =
|
||||
{
|
||||
new object?[] { ":", false, null, null },
|
||||
new object?[] { "1", true, new TimeSpan(0, 0, 1, 0), null },
|
||||
new object?[] { "99", true, new TimeSpan(0, 0, 99, 0), null },
|
||||
new object?[] { "300", false, null, null },
|
||||
new object?[] { "1:2", true, new TimeSpan(0, 0, 1, 2), null },
|
||||
new object?[] { "1:02", true, new TimeSpan(0, 0, 1, 2), null },
|
||||
new object?[] { "1:92", false, null, null },
|
||||
new object?[] { "1:002", false, null, null },
|
||||
new object?[] { "1:02:3", true, new TimeSpan(0, 0, 1, 2, 3), null },
|
||||
new object?[] { "1:02:300", true, new TimeSpan(0, 0, 1, 2, 300), null },
|
||||
new object?[] { "1:02:3000", false, null, null },
|
||||
new object?[] { "1:02:300 ()", false, null, null },
|
||||
new object?[] { "1:02:300 (1,2,3)", true, new TimeSpan(0, 0, 1, 2, 300), "1,2,3" },
|
||||
};
|
||||
|
||||
[TestCaseSource(nameof(test_cases))]
|
||||
public void TestTryParse(string timestamp, bool expectedSuccess, TimeSpan? expectedParsedTime, string? expectedSelection)
|
||||
{
|
||||
bool actualSuccess = EditorTimestampParser.TryParse(timestamp, out var actualParsedTime, out string? actualSelection);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(actualSuccess, Is.EqualTo(expectedSuccess));
|
||||
Assert.That(actualParsedTime, Is.EqualTo(expectedParsedTime));
|
||||
Assert.That(actualSelection, Is.EqualTo(expectedSelection));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -271,7 +271,7 @@ namespace osu.Game.Online.Chat
|
||||
handleAdvanced(advanced_link_regex, result, startIndex);
|
||||
|
||||
// handle editor times
|
||||
handleMatches(EditorTimestampParser.TIME_REGEX, "{0}", $@"{OsuGameBase.OSU_PROTOCOL}edit/{{0}}", result, startIndex, LinkAction.OpenEditorTimestamp);
|
||||
handleMatches(EditorTimestampParser.TIME_REGEX_STRICT, "{0}", $@"{OsuGameBase.OSU_PROTOCOL}edit/{{0}}", result, startIndex, LinkAction.OpenEditorTimestamp);
|
||||
|
||||
// handle channels
|
||||
handleMatches(channel_regex, "{0}", $@"{OsuGameBase.OSU_PROTOCOL}chan/{{0}}", result, startIndex, LinkAction.OpenChannel);
|
||||
|
@ -9,13 +9,34 @@ namespace osu.Game.Rulesets.Edit
|
||||
{
|
||||
public static class EditorTimestampParser
|
||||
{
|
||||
// 00:00:000 (...) - test
|
||||
// original osu-web regex: https://github.com/ppy/osu-web/blob/3b1698639244cfdaf0b41c68bfd651ea729ec2e3/resources/js/utils/beatmapset-discussion-helper.ts#L78
|
||||
public static readonly Regex TIME_REGEX = new Regex(@"\b(((?<minutes>\d{2,}):(?<seconds>[0-5]\d)[:.](?<milliseconds>\d{3}))(?<selection>\s\([^)]+\))?)", RegexOptions.Compiled);
|
||||
/// <summary>
|
||||
/// Used for parsing in contexts where we don't want e.g. normal times of day to be parsed as timestamps (e.g. chat)
|
||||
/// Original osu-web regex: https://github.com/ppy/osu-web/blob/3b1698639244cfdaf0b41c68bfd651ea729ec2e3/resources/js/utils/beatmapset-discussion-helper.ts#L78
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// 00:00:000 (...) - test
|
||||
/// </example>
|
||||
public static readonly Regex TIME_REGEX_STRICT = new Regex(@"\b(((?<minutes>\d{2,}):(?<seconds>[0-5]\d)[:.](?<milliseconds>\d{3}))(?<selection>\s\([^)]+\))?)", RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
||||
/// Used for editor-specific context wherein we want to try as hard as we can to process user input as a timestamp.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <list type="bullet">
|
||||
/// <item>1 - parses to 01:00:000</item>
|
||||
/// <item>1:2 - parses to 01:02:000</item>
|
||||
/// <item>1:02 - parses to 01:02:000</item>
|
||||
/// <item>1:92 - does not parse</item>
|
||||
/// <item>1:02:3 - parses to 01:02:003</item>
|
||||
/// <item>1:02:300 - parses to 01:02:300</item>
|
||||
/// <item>1:02:300 (1,2,3) - parses to 01:02:300 with selection</item>
|
||||
/// </list>
|
||||
/// </example>
|
||||
private static readonly Regex time_regex_lenient = new Regex(@"^(((?<minutes>\d{1,3})(:(?<seconds>([0-5]?\d))([:.](?<milliseconds>\d{0,3}))?)?)(?<selection>\s\([^)]+\))?)$", RegexOptions.Compiled);
|
||||
|
||||
public static bool TryParse(string timestamp, [NotNullWhen(true)] out TimeSpan? parsedTime, out string? parsedSelection)
|
||||
{
|
||||
Match match = TIME_REGEX.Match(timestamp);
|
||||
Match match = time_regex_lenient.Match(timestamp);
|
||||
|
||||
if (!match.Success)
|
||||
{
|
||||
@ -24,16 +45,14 @@ namespace osu.Game.Rulesets.Edit
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
int timeMin, timeSec, timeMsec;
|
||||
|
||||
result &= int.TryParse(match.Groups[@"minutes"].Value, out int timeMin);
|
||||
result &= int.TryParse(match.Groups[@"seconds"].Value, out int timeSec);
|
||||
result &= int.TryParse(match.Groups[@"milliseconds"].Value, out int timeMsec);
|
||||
int.TryParse(match.Groups[@"minutes"].Value, out timeMin);
|
||||
int.TryParse(match.Groups[@"seconds"].Value, out timeSec);
|
||||
int.TryParse(match.Groups[@"milliseconds"].Value, out timeMsec);
|
||||
|
||||
// somewhat sane limit for timestamp duration (10 hours).
|
||||
result &= timeMin < 600;
|
||||
|
||||
if (!result)
|
||||
if (timeMin >= 600)
|
||||
{
|
||||
parsedTime = null;
|
||||
parsedSelection = null;
|
||||
@ -42,8 +61,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
|
||||
parsedTime = TimeSpan.FromMinutes(timeMin) + TimeSpan.FromSeconds(timeSec) + TimeSpan.FromMilliseconds(timeMsec);
|
||||
parsedSelection = match.Groups[@"selection"].Value.Trim();
|
||||
if (!string.IsNullOrEmpty(parsedSelection))
|
||||
parsedSelection = parsedSelection[1..^1];
|
||||
parsedSelection = !string.IsNullOrEmpty(parsedSelection) ? parsedSelection[1..^1] : null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user