1
0
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:
Bartłomiej Dach 2019-09-20 20:55:30 +02:00
parent db90d211cb
commit 33c51d5178
2 changed files with 139 additions and 130 deletions

View File

@ -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;
}
}
}
}

View 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;
}
}
}
}