mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 14:17:26 +08:00
Extract parsing filter queries to class
For the sake of testability without having to spin up visual tests, extract methods related to parsing filter queries from FilterControl to a static FilterQueryParser class.
This commit is contained in:
parent
db90d211cb
commit
33c51d5178
@ -16,8 +16,6 @@ using Container = osu.Framework.Graphics.Containers.Container;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets;
|
||||
using System.Text.RegularExpressions;
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Screens.Select
|
||||
{
|
||||
@ -47,10 +45,7 @@ namespace osu.Game.Screens.Select
|
||||
Ruleset = ruleset.Value
|
||||
};
|
||||
|
||||
applyQueries(criteria, ref query);
|
||||
|
||||
criteria.SearchText = query;
|
||||
|
||||
FilterQueryParser.ApplyQueries(criteria, query);
|
||||
return criteria;
|
||||
}
|
||||
|
||||
@ -181,129 +176,5 @@ namespace osu.Game.Screens.Select
|
||||
}
|
||||
|
||||
private void updateCriteria() => FilterChanged?.Invoke(CreateCriteria());
|
||||
|
||||
private static readonly Regex query_syntax_regex = new Regex(
|
||||
@"\b(?<key>stars|ar|dr|cs|divisor|length|objects|bpm|status)(?<op>[=:><]+)(?<value>\S*)",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private void applyQueries(FilterCriteria criteria, ref string query)
|
||||
{
|
||||
foreach (Match match in query_syntax_regex.Matches(query))
|
||||
{
|
||||
var key = match.Groups["key"].Value.ToLower();
|
||||
var op = match.Groups["op"].Value;
|
||||
var value = match.Groups["value"].Value;
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case "stars" when float.TryParse(value, out var stars):
|
||||
updateCriteriaRange(ref criteria.StarDifficulty, op, stars);
|
||||
break;
|
||||
|
||||
case "ar" when float.TryParse(value, out var ar):
|
||||
updateCriteriaRange(ref criteria.ApproachRate, op, ar);
|
||||
break;
|
||||
|
||||
case "dr" when float.TryParse(value, out var dr):
|
||||
updateCriteriaRange(ref criteria.DrainRate, op, dr);
|
||||
break;
|
||||
|
||||
case "cs" when float.TryParse(value, out var cs):
|
||||
updateCriteriaRange(ref criteria.CircleSize, op, cs);
|
||||
break;
|
||||
|
||||
case "bpm" when double.TryParse(value, out var bpm):
|
||||
updateCriteriaRange(ref criteria.BPM, op, bpm);
|
||||
break;
|
||||
|
||||
case "length" when double.TryParse(value.TrimEnd('m', 's', 'h'), out var length):
|
||||
var scale =
|
||||
value.EndsWith("ms") ? 1 :
|
||||
value.EndsWith("s") ? 1000 :
|
||||
value.EndsWith("m") ? 60000 :
|
||||
value.EndsWith("h") ? 3600000 : 1000;
|
||||
|
||||
updateCriteriaRange(ref criteria.Length, op, length * scale, scale / 2.0);
|
||||
break;
|
||||
|
||||
case "divisor" when int.TryParse(value, out var divisor):
|
||||
updateCriteriaRange(ref criteria.BeatDivisor, op, divisor);
|
||||
break;
|
||||
|
||||
case "status" when Enum.TryParse<BeatmapSetOnlineStatus>(value, true, out var statusValue):
|
||||
updateCriteriaRange(ref criteria.OnlineStatus, op, statusValue);
|
||||
break;
|
||||
}
|
||||
|
||||
query = query.Replace(match.ToString(), "");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCriteriaRange(ref FilterCriteria.OptionalRange<float> range, string op, float value, float tolerance = 0.05f)
|
||||
{
|
||||
updateCriteriaRange(ref range, op, value);
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case "=":
|
||||
case ":":
|
||||
range.Min = value - tolerance;
|
||||
range.Max = value + tolerance;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCriteriaRange(ref FilterCriteria.OptionalRange<double> range, string op, double value, double tolerance = 0.05)
|
||||
{
|
||||
updateCriteriaRange(ref range, op, value);
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case "=":
|
||||
case ":":
|
||||
range.Min = value - tolerance;
|
||||
range.Max = value + tolerance;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCriteriaRange<T>(ref FilterCriteria.OptionalRange<T> range, string op, T value)
|
||||
where T : struct, IComparable
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
default:
|
||||
return;
|
||||
|
||||
case "=":
|
||||
case ":":
|
||||
range.IsInclusive = true;
|
||||
range.Min = value;
|
||||
range.Max = value;
|
||||
break;
|
||||
|
||||
case ">":
|
||||
range.IsInclusive = false;
|
||||
range.Min = value;
|
||||
break;
|
||||
|
||||
case ">=":
|
||||
case ">:":
|
||||
range.IsInclusive = true;
|
||||
range.Min = value;
|
||||
break;
|
||||
|
||||
case "<":
|
||||
range.IsInclusive = false;
|
||||
range.Max = value;
|
||||
break;
|
||||
|
||||
case "<=":
|
||||
case "<:":
|
||||
range.IsInclusive = true;
|
||||
range.Max = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
138
osu.Game/Screens/Select/FilterQueryParser.cs
Normal file
138
osu.Game/Screens/Select/FilterQueryParser.cs
Normal file
@ -0,0 +1,138 @@
|
||||
// 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 System.Text.RegularExpressions;
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Screens.Select
|
||||
{
|
||||
internal static class FilterQueryParser
|
||||
{
|
||||
private static readonly Regex query_syntax_regex = new Regex(
|
||||
@"\b(?<key>stars|ar|dr|cs|divisor|length|objects|bpm|status)(?<op>[=:><]+)(?<value>\S*)",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
internal static void ApplyQueries(FilterCriteria criteria, string query)
|
||||
{
|
||||
foreach (Match match in query_syntax_regex.Matches(query))
|
||||
{
|
||||
var key = match.Groups["key"].Value.ToLower();
|
||||
var op = match.Groups["op"].Value;
|
||||
var value = match.Groups["value"].Value;
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case "stars" when float.TryParse(value, out var stars):
|
||||
updateCriteriaRange(ref criteria.StarDifficulty, op, stars);
|
||||
break;
|
||||
|
||||
case "ar" when float.TryParse(value, out var ar):
|
||||
updateCriteriaRange(ref criteria.ApproachRate, op, ar);
|
||||
break;
|
||||
|
||||
case "dr" when float.TryParse(value, out var dr):
|
||||
updateCriteriaRange(ref criteria.DrainRate, op, dr);
|
||||
break;
|
||||
|
||||
case "cs" when float.TryParse(value, out var cs):
|
||||
updateCriteriaRange(ref criteria.CircleSize, op, cs);
|
||||
break;
|
||||
|
||||
case "bpm" when double.TryParse(value, out var bpm):
|
||||
updateCriteriaRange(ref criteria.BPM, op, bpm);
|
||||
break;
|
||||
|
||||
case "length" when double.TryParse(value.TrimEnd('m', 's', 'h'), out var length):
|
||||
var scale =
|
||||
value.EndsWith("ms") ? 1 :
|
||||
value.EndsWith("s") ? 1000 :
|
||||
value.EndsWith("m") ? 60000 :
|
||||
value.EndsWith("h") ? 3600000 : 1000;
|
||||
|
||||
updateCriteriaRange(ref criteria.Length, op, length * scale, scale / 2.0);
|
||||
break;
|
||||
|
||||
case "divisor" when int.TryParse(value, out var divisor):
|
||||
updateCriteriaRange(ref criteria.BeatDivisor, op, divisor);
|
||||
break;
|
||||
|
||||
case "status" when Enum.TryParse<BeatmapSetOnlineStatus>(value, true, out var statusValue):
|
||||
updateCriteriaRange(ref criteria.OnlineStatus, op, statusValue);
|
||||
break;
|
||||
}
|
||||
|
||||
query = query.Replace(match.ToString(), "");
|
||||
}
|
||||
|
||||
criteria.SearchText = query;
|
||||
}
|
||||
|
||||
private static void updateCriteriaRange(ref FilterCriteria.OptionalRange<float> range, string op, float value, float tolerance = 0.05f)
|
||||
{
|
||||
updateCriteriaRange(ref range, op, value);
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case "=":
|
||||
case ":":
|
||||
range.Min = value - tolerance;
|
||||
range.Max = value + tolerance;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateCriteriaRange(ref FilterCriteria.OptionalRange<double> range, string op, double value, double tolerance = 0.05)
|
||||
{
|
||||
updateCriteriaRange(ref range, op, value);
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case "=":
|
||||
case ":":
|
||||
range.Min = value - tolerance;
|
||||
range.Max = value + tolerance;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateCriteriaRange<T>(ref FilterCriteria.OptionalRange<T> range, string op, T value)
|
||||
where T : struct, IComparable
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
default:
|
||||
return;
|
||||
|
||||
case "=":
|
||||
case ":":
|
||||
range.IsInclusive = true;
|
||||
range.Min = value;
|
||||
range.Max = value;
|
||||
break;
|
||||
|
||||
case ">":
|
||||
range.IsInclusive = false;
|
||||
range.Min = value;
|
||||
break;
|
||||
|
||||
case ">=":
|
||||
case ">:":
|
||||
range.IsInclusive = true;
|
||||
range.Min = value;
|
||||
break;
|
||||
|
||||
case "<":
|
||||
range.IsInclusive = false;
|
||||
range.Max = value;
|
||||
break;
|
||||
|
||||
case "<=":
|
||||
case "<:":
|
||||
range.IsInclusive = true;
|
||||
range.Max = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user