Refactor console client to new Govor.ConsoleClient project

Removed Govor.Console implementation and introduced a new Govor.ConsoleClient project with a modular command-based architecture. Added dependency injection, command dispatcher, middleware pipeline, logging, and interactive command support. Updated solution and project files to reflect the new structure.
This commit is contained in:
Artemy
2025-07-19 19:08:21 +07:00
parent ff873ae179
commit f524a9f0b7
21 changed files with 410 additions and 268 deletions
@@ -0,0 +1,17 @@
using Govor.ConsoleClient.Commands;
namespace Govor.ConsoleClient.Services;
public class CommandContext
{
public string Route { get; }
public string? Arguments { get; }
public ICommand Command { get; }
public CommandContext(string route, string? arguments, ICommand command)
{
Route = route;
Arguments = arguments;
Command = command;
}
}
@@ -0,0 +1,41 @@
using System.Reflection;
using Govor.ConsoleClient.Commands;
namespace Govor.ConsoleClient.Services;
public class CommandDispatcher
{
private readonly Dictionary<string, ICommand> _commands = new();
private readonly ILogger _logger;
private readonly MiddlewarePipeline _pipeline;
public CommandDispatcher(IEnumerable<ICommand> commands, ILogger logger, MiddlewarePipeline pipeline)
{
_logger = logger;
_pipeline = pipeline;
foreach (var command in commands)
{
var route = command.GetType().GetCustomAttribute<CommandRouteAttribute>()?.Path.Replace("/","")
?? command.GetType().Name.Replace("Command", "").ToLower();
_commands[route.ToLower()] = command;
}
}
public async Task<ICommand?> DispatchAsync(string input)
{
var args = input.Split(' ', 2);
var cmd = args[0].ToLower();
if (_commands.TryGetValue(cmd, out var command))
{
var context = new CommandContext(cmd, args.Length > 1 ? args[1] : null, command);
await _pipeline.ExecuteAsync(context);
return command;
}
else
{
_logger.Warn("Неизвестная команда. Введите '/help'.");
return null;
}
}
}
@@ -0,0 +1,45 @@
namespace Govor.ConsoleClient.Services;
public class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.ResetColor();
Console.WriteLine(message);
}
public void Info(string message)
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"[INFO] {message}");
Console.ResetColor();
}
public void Warn(string message)
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($"[WARN] {message}");
Console.ResetColor();
}
public void Error(string message)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"[ERROR] {message}");
Console.ResetColor();
}
public void Title(string message)
{
var upper = message.ToUpper();
var length = upper.Length + 6;
var border = new string('=', length);
var padded = $"= {upper} =";
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine(border);
Console.WriteLine(padded);
Console.WriteLine(border);
Console.ResetColor();
}
}
+10
View File
@@ -0,0 +1,10 @@
namespace Govor.ConsoleClient.Services;
public interface ILogger
{
void Log(string message);
void Info(string message);
void Warn(string message);
void Error(string message);
void Title(string title);
}
@@ -0,0 +1,49 @@
using Govor.ConsoleClient.Commands;
namespace Govor.ConsoleClient.Services;
public class InputPipeline
{
private readonly CommandDispatcher _dispatcher;
private readonly ILogger _logger;
private IInteractiveCommand? _activeCommand;
public InputPipeline(CommandDispatcher dispatcher, ILogger logger)
{
_dispatcher = dispatcher;
_logger = logger;
}
public async Task ProcessInputAsync(string input)
{
if (string.IsNullOrWhiteSpace(input)) return;
if (input.StartsWith("/"))
{
_activeCommand = null; // Сброс активной команды
var commandInput = input[1..];
var result = await _dispatcher.DispatchAsync(commandInput);
// Если команда поддерживает интерактивность, сохраняем как активную
if (result is IInteractiveCommand interactiveCommand && !interactiveCommand.IsCompleted)
{
_activeCommand = interactiveCommand;
}
}
else
{
if (_activeCommand != null)
{
await _activeCommand.HandleInputAsync(input);
if (_activeCommand.IsCompleted)
_activeCommand = null;
}
else
{
_logger.Info($"[Ввод пользователя]: {input}");
}
}
}
}
@@ -0,0 +1,23 @@
namespace Govor.ConsoleClient.Services.Middleware;
public class ExceptionHandlingMiddleware : ICommandMiddleware
{
private readonly ILogger _logger;
public ExceptionHandlingMiddleware(ILogger logger)
{
_logger = logger;
}
public async Task InvokeAsync(CommandContext context, Func<Task> next)
{
try
{
await next();
}
catch (Exception ex)
{
_logger.Error($"Произошла ошибка при выполнении команды '{context.Route}': {ex.Message}");
}
}
}
@@ -0,0 +1,6 @@
namespace Govor.ConsoleClient.Services.Middleware;
public interface ICommandMiddleware
{
Task InvokeAsync(CommandContext context, Func<Task> next);
}
@@ -0,0 +1,29 @@
using Govor.ConsoleClient.Services.Middleware;
namespace Govor.ConsoleClient.Services;
public delegate Task CommandMiddleware(CommandContext context, Func<Task> next);
public class MiddlewarePipeline
{
private readonly IList<ICommandMiddleware> _middlewares;
public MiddlewarePipeline(IEnumerable<ICommandMiddleware> middlewares)
{
_middlewares = middlewares.ToList();
}
public Task ExecuteAsync(CommandContext context)
{
return InvokeNext(0, context);
}
private Task InvokeNext(int index, CommandContext context)
{
if (index < _middlewares.Count)
{
return _middlewares[index].InvokeAsync(context, () => InvokeNext(index + 1, context));
}
return context.Command.ExecuteAsync(context);
}
}