Реальные цвета на реальных терминалах

Пишу какую-нибудь забавную утилиту, но с серьезной реализацией. Я решил добавить цвета к ошибкам.

Что можно сделать проще?

[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, и он все еще показывает цвета! Эм-м-м…