cli/
lw.rs

1use std::io::{Write, stdout};
2
3use termcolor::{self, ColorChoice, ColorSpec, StandardStream, WriteColor};
4use tracing::{Level, event};
5
6use base::charset::{self, DescribedChar, LincolnChar, LincolnState};
7
8pub struct LincolnStreamWriter {
9    current_attributes: LincolnState,
10    stream: StandardStream,
11}
12
13fn get_colour_choice() -> termcolor::ColorChoice {
14    if atty::is(atty::Stream::Stdout) {
15        ColorChoice::Auto
16    } else {
17        ColorChoice::Never
18    }
19}
20
21impl LincolnStreamWriter {
22    pub fn new() -> LincolnStreamWriter {
23        LincolnStreamWriter {
24            current_attributes: LincolnState::default(),
25            stream: StandardStream::stdout(get_colour_choice()),
26        }
27    }
28
29    fn set_lw_colour(&mut self, col: charset::Colour) {
30        let mut new_colour = ColorSpec::new();
31        match col {
32            charset::Colour::Black => {
33                new_colour
34                    .set_fg(Some(termcolor::Color::White))
35                    .set_bg(Some(termcolor::Color::Black));
36            }
37            charset::Colour::Red => {
38                new_colour
39                    .set_fg(Some(termcolor::Color::Black))
40                    .set_bg(Some(termcolor::Color::Black));
41            }
42        }
43        if let Err(e) = self.stream.set_color(&new_colour) {
44            event!(
45                Level::ERROR,
46                "Failed to select colour {:?}: {}",
47                new_colour,
48                e
49            );
50        }
51    }
52
53    pub fn write(&mut self, item: DescribedChar) -> Result<(), std::io::Error> {
54        let prev_colour = self.current_attributes.colour;
55        if item.attributes.colour != prev_colour {
56            self.set_lw_colour(item.attributes.colour);
57        }
58        let to_emit: char = match item {
59            DescribedChar {
60                unicode_representation: None,
61                base_char: LincolnChar::Unprintable(_),
62                ..
63            } => {
64                return Ok(());
65            }
66            DescribedChar {
67                unicode_representation: Some('\r'),
68                ..
69            } => '\n',
70            DescribedChar {
71                unicode_representation: Some(ch),
72                ..
73            } => ch,
74            DescribedChar {
75                unicode_representation: None,
76                base_char: LincolnChar::UnicodeBaseChar(base),
77                ..
78            } => {
79                // We just emit it in normal script.
80                base
81            }
82        };
83        let stdout = stdout();
84        let mut handle = stdout.lock();
85        write!(handle, "{to_emit}").and_then(|()| handle.flush())
86    }
87
88    pub fn disconnect(&mut self) {
89        if let Err(e) = self.stream.reset() {
90            event!(Level::ERROR, "Failed to reset terminal: {}", e);
91        }
92    }
93}