This commit is contained in:
59
BotPages.Core/Routing/CommandsRegistry.cs
Normal file
59
BotPages.Core/Routing/CommandsRegistry.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
namespace BotPages.Core.Routing;
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
/// <summary>
|
||||
/// Реестр команд, доступных из любого места.
|
||||
/// </summary>
|
||||
internal sealed class CommandsRegistry
|
||||
{
|
||||
private readonly List<(Regex pattern, Func<PageContext, CancellationToken, Task> handler)> _commands = new();
|
||||
|
||||
/// <summary>
|
||||
/// Зарегистрировать команду, ведущую на страницу.
|
||||
/// </summary>
|
||||
public CommandsRegistry Map<TPage>(string commandTemplate) where TPage : Page
|
||||
{
|
||||
var pattern = ToRegex(commandTemplate);
|
||||
_commands.Add((pattern, (ctx, ct) => ctx.Navigation.GoToAsync<TPage>(ctx, ct)));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Зарегистрировать команду с кастомным обработчиком.
|
||||
/// </summary>
|
||||
public CommandsRegistry Map(string commandTemplate, Func<PageContext, CancellationToken, Task> handler)
|
||||
{
|
||||
var pattern = ToRegex(commandTemplate);
|
||||
_commands.Add((pattern, handler));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Попробовать выполнить команду.
|
||||
/// </summary>
|
||||
public bool TryDispatch(PageContext ctx, string command, CancellationToken ct, out Task? task)
|
||||
{
|
||||
foreach (var (pattern, handler) in _commands)
|
||||
{
|
||||
if (pattern.IsMatch(command))
|
||||
{
|
||||
task = handler(ctx, ct);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
task = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Regex ToRegex(string template)
|
||||
{
|
||||
// Простейшее преобразование шаблона: "/open {page} {id?}" -> Regex
|
||||
var escaped = Regex.Escape(template)
|
||||
.Replace("\\{", "{").Replace("\\}", "}");
|
||||
var pattern = "^" + escaped
|
||||
.Replace("{page}", "(?<page>\\S+)")
|
||||
.Replace("{id?}", "(?<id>\\S+)?") + "$";
|
||||
return new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
}
|
||||
}
|
||||
18
BotPages.Core/Routing/RouteAttribute.cs
Normal file
18
BotPages.Core/Routing/RouteAttribute.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace BotPages.Core.Routing;
|
||||
|
||||
/// <summary>
|
||||
/// Атрибут для декларативного указания маршрута страницы.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
public sealed class RouteAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Шаблон маршрута.
|
||||
/// </summary>
|
||||
public string Template { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Создать атрибут маршрута.
|
||||
/// </summary>
|
||||
public RouteAttribute(string template) => Template = template;
|
||||
}
|
||||
29
BotPages.Core/Routing/RoutesRegistry.cs
Normal file
29
BotPages.Core/Routing/RoutesRegistry.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
namespace BotPages.Core.Routing;
|
||||
|
||||
/// <summary>
|
||||
/// Реестр маршрутов страниц.
|
||||
/// </summary>
|
||||
internal sealed class RoutesRegistry
|
||||
{
|
||||
private readonly Dictionary<string, Type> _routes = new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Зарегистрировать маршрут для страницы.
|
||||
/// </summary>
|
||||
public void Map<TPage>(string template) where TPage : Page
|
||||
{
|
||||
if (_routes.ContainsKey(template))
|
||||
throw new InvalidOperationException($"Route '{template}' is already mapped.");
|
||||
_routes[template] = typeof(TPage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Найти страницу по маршруту.
|
||||
/// </summary>
|
||||
public Type? Resolve(string template) => _routes.TryGetValue(template, out var t) ? t : null;
|
||||
|
||||
/// <summary>
|
||||
/// Получить снимок всех маршрутов.
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<string, Type> Snapshot() => _routes;
|
||||
}
|
||||
Reference in New Issue
Block a user