CLI приложения #9. Spectre.Console
В предыдущих постах мы занимались ручной покраской вывода консоли. Это полезно - знать, как всё устроено и уметь делать это руками. Но, как это часто бывает, всё хорошее уже придумано до нас и пользоваться мы будем дальше готовой библиотекой Spectre.Console
Установка:
dotnet add package Spectre.Console
Помните пример из седьмого поста:
Console.WriteLine("\u001b[31mОшибка\u001b[0m: файл не найден");
Он же превращается в одну строку:
AnsiConsole.MarkupLine("[red]Ошибка[/]: файл не найден");
Разметка похожа на BBCode: открывающий тег со стилем, закрывающий — просто [/]. Внутри одного тега можно перечислять что угодно:
AnsiConsole.MarkupLine("[bold green]Готово[/]");
AnsiConsole.MarkupLine("[yellow on blue]Предупреждение[/]");
AnsiConsole.MarkupLine("[rgb(0,122,204)]Фирменный цвет[/]");
AnsiConsole.MarkupLine("[link=https://spectreconsole.net]Документация[/]");
Но главное здесь даже не краткость. В прошлом посте рассматривалось множество строк кода — проверки NO_COLOR, TERM, перенаправления. Библиотека делает всё это сама. Более того, она смотрит, сколько цветов терминал реально поддерживает, и подбирает ближайший доступный: попросили RGB, а терминал умеет шестнадцать цветов — получите похожий из шестнадцати. Вывод перенаправили в файл — разметка просто исчезнет, останется чистый текст.
А вот этой командой можно посмотреть, что о конкретном терминале знает библиотека:
AnsiConsole.WriteLine(AnsiConsole.Profile.Capabilities.ColorSystem.ToString());
Важно!
Никогда не подставляйте в разметку данные от пользователя напрямую. Строка вида
Файл [config] не найденсломает разбор, а в худшем случае имя файла станет управляющей командой. Для чужих строк есть экранирование:
AnsiConsole.MarkupLine($"Файл [red]{Markup.Escape(fileName)}[/] не найден");
Ровно та же логика, что с параметрами SQL-запроса: свой шаблон — отдельно, чужие данные — отдельно.
Теперь про то, о чём мы говорили в шестой части. По умолчанию AnsiConsole пишет в stdout, и сообщения об ошибках туда отправлять не стоит. Заводим отдельную консоль для второго потока:
var errorConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
Out = new AnsiConsoleOutput(Console.Error)
});
errorConsole.MarkupLine("[red]Ошибка[/]: файл не найден");
Приятная мелочь напоследок — вывод исключений:
catch (Exception ex)
{
AnsiConsole.WriteException(ex, ExceptionFormats.ShortenPaths);
}
Стек становится читаемым: типы, методы и пути подсвечены по-разному, а длинные пути сокращены. После стандартного полотна из ex.ToString() — совсем другое ощущение.
Важно!
Не перепутайте пакеты.
Spectre.Console— это вывод: цвет, таблицы, прогресс-бары. АSpectre.Console.Cli— отдельная библиотека для разбора аргументов, то есть альтернативаSystem.CommandLine. Живёт в другом репозитории, версии у неё свои. Нам сейчас нужен первый.
В следующем посте соберём из этого нормальный вывод: таблицы, рамки и заголовки.