Реальные цвета на реальных терминалах
Пишу какую-нибудь забавную утилиту, но с серьезной реализацией. Я решил добавить цвета к ошибкам.
Что можно сделать проще?
[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, и он все еще показывает цвета! Эм-м-м…