84 lines
3.0 KiB
C#
84 lines
3.0 KiB
C#
namespace BotPages.Core.Routing;
|
||
|
||
using System.Text.RegularExpressions;
|
||
|
||
|
||
/// <summary>
|
||
/// Реестр команд, доступных из любого места.
|
||
/// </summary>
|
||
internal sealed class CommandsRegistry
|
||
{
|
||
private readonly List<Command> _commands = new();
|
||
|
||
public List<Command> Commands => _commands;
|
||
|
||
/// <summary>
|
||
/// Зарегистрировать команду, ведущую на страницу.
|
||
/// </summary>
|
||
public CommandsRegistry Map<TPage>(string commandTemplate, bool publish = false, string? description = null) where TPage : Page
|
||
{
|
||
var pattern = ToRegex(commandTemplate);
|
||
|
||
return Map(commandTemplate, (ctx, args, ct) => ctx.Navigation.GoToAsync<TPage>(ctx, ct), publish, description);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Зарегистрировать команду с кастомным обработчиком.
|
||
/// </summary>
|
||
public CommandsRegistry Map(string commandTemplate, CommandHandler handler, bool publish = false, string? description = null)
|
||
{
|
||
var pattern = ToRegex(commandTemplate);
|
||
_commands.Add(new Command()
|
||
{
|
||
Name = ToCommandName(commandTemplate),
|
||
Pattern = pattern,
|
||
Handler = handler,
|
||
Publish = publish,
|
||
Description = string.IsNullOrWhiteSpace(description) ? null : description
|
||
});
|
||
return this;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Попробовать выполнить команду.
|
||
/// </summary>
|
||
public bool TryDispatch(PageContext ctx, string command, CancellationToken ct, out Task? task)
|
||
{
|
||
foreach (var cmd in _commands)
|
||
{
|
||
var match = cmd.Pattern.Match(command);
|
||
if (match.Success)
|
||
{
|
||
// Собираем аргументы
|
||
var args = cmd.Pattern.GetGroupNames()
|
||
.Where(n => n != "0")
|
||
.Select(n => new { n, v = match.Groups[n].Value })
|
||
.Where(x => !string.IsNullOrEmpty(x.v))
|
||
.ToDictionary(x => x.n, x => x.v);
|
||
|
||
task = cmd.Handler(ctx, args, 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);
|
||
}
|
||
|
||
private static string ToCommandName(string template)
|
||
{
|
||
// Простейшее преобразование шаблона: "/open {page} {id?}" -> "/open"
|
||
return template.Split(" ", StringSplitOptions.RemoveEmptyEntries).First().ToLowerInvariant();
|
||
}
|
||
}
|