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 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}