namespace BotPages.Core.Routing; using System.Text.RegularExpressions; /// /// Реестр команд, доступных из любого места. /// internal sealed class CommandsRegistry { private readonly List _commands = new(); public List Commands => _commands; /// /// Зарегистрировать команду, ведущую на страницу. /// public CommandsRegistry Map(string commandTemplate, bool publish = false, string? description = null) where TPage : Page { var pattern = ToRegex(commandTemplate); return Map(commandTemplate, (ctx, args, ct) => ctx.Navigation.GoToAsync(ctx, ct), publish, description); } /// /// Зарегистрировать команду с кастомным обработчиком. /// 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; } /// /// Попробовать выполнить команду. /// 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}", "(?\\S+)") .Replace("{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(); } }