Реальные цвета на реальных терминалах
Пишу какую-нибудь забавную утилиту, но с серьезной реализацией. Я решил добавить цвета к ошибкам.
Что можно сделать проще?
[dependencies] colored = "2"
и
Err(e) => { eprintln!("{}", e.to_string().red()); }
Цветной режим поддерживает автоматическое определение NO_COLOR
, значит ли это, что я получил «красное» сообщение производственного уровня?
Неа
foo (colored output in stderr)
но
foo > /dev/null (no colors in stderr)
Почему? Потому что colored
не может видеть, печатаю ли я в stdout
или stderr
, и проверяет stdout
на наличие tty
. Это не так, поэтому сообщение не должно иметь цвета. Но! У меня tty
за stderr
, хотя tty
за stdout
нет.
Бездельничать!
Кто сказал, что производство простое?
Следующую библиотеку (termcolor
), которую я пытаюсь использовать, гораздо сложнее:
use std::io::{self, Write};
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
fn write_green() -> io::Result<()> {
let mut stdout = StandardStream::stdout(ColorChoice::Always);
stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?;
writeln!(&mut stdout, "green text!")
}
и он не обнаруживает atty. Чисто в документах. Хм. Нет. Следующий owo-colors
.
foo > /dev/null
дайте мне цвета, хорошо. Но foo 2> foobar
выдал мне esc-коды в файле. Не хорошо. Есть ли у них какой-либо вспомогательный код, чтобы определить, нужны ли цвета?
println!( "{}", "colored blue if a supported terminal". .if_supports_color(Stream::Stdout, |text| text.bright_blue()) );
Но это не работает! Прямой копипаст из примеров даже не компилируется.
О, нам нужна функция «supports-color». Я включил его, но он все еще не компилируется. Поток не найден.
Я ковырялся, сколько мог, нет, спасибо. Следующий.
colour
ящик, даже не говоря об автоопределении…
Ладно, вернемся. Существует colored
, в котором отсутствует надлежащее обнаружение stderr
. Но есть ящик supports-color
, который я нашел ранее внутри owo-colors
. Давайте бросим вместе color
и supports-color
:
let msg = if let Some(support) = supports_color::on(Stream::Stdout){ e.to_string().red() }else{ e.to_string() };
Агр… String
и ColoredString
— разные типы. Могу ли я сделать ColoredString
без цветов? Неа.
Хорошо, давайте сделаем это прямолинейно:
if supports_color::on(Stream::Stderr).is_some(){ eprintln!("{}", e.to_string().red()); }else{ eprintln!("{}", e); };
Но он не проходит тест foo >/dev/null
, потому что «красный» думает, что stdout
не является tty
, следовательно, нет цветов.
Аррр…
colored::control::set_override( supports_color::on(Stream::Stderr).is_some() ); eprintln!("{}", e.to_string().red());
Ладно, обычные тесты прошли.
Все редиректы работают. Переменная среды NO_COLOR
соблюдается.
Но могу ли я заставить цвет? Да, есть FORCE_COLOR
!
Бинго. Теперь появляется сообщение об ошибке рабочего уровня.
В Rust мне потребовалось около часа, чтобы написать правильное цветное сообщение на экране. Хм. Серийный уровень — это сложно…
P.S.
Я обнаружил, что он не полностью соблюдает TERM. Я пробовал vt100, и он все еще показывает цвета! Эм-м-м…