1use crate::{Error, Target};
4use std::sync::Arc;
5use termcolor::{ColorChoice, ColorSpec, StandardStream, WriteColor};
6
7pub fn stdout() -> Result<Target, Error> {
9    let formatter = Formatter::init_with(|| Ok(StandardStream::stdout(ColorChoice::Auto)))?;
10    Ok(Target::human(formatter))
11}
12
13pub use self::test_helper::{test, test_with_color};
14
15#[derive(Clone)]
17pub struct Formatter {
18    inner: Arc<InternalFormatter>,
19}
20
21impl Formatter {
22    pub(crate) fn init_with<W: WriteColor, F: FnOnce() -> Result<W, Error> + Send + 'static>(
23        init: F,
24    ) -> Result<Self, Error> {
25        Ok(Formatter {
26            inner: Arc::new(InternalFormatter::init_with(init)?),
27        })
28    }
29
30    pub fn write<D: Into<Vec<u8>>>(&self, data: D) -> Result<(), Error> {
32        self.send(Message::Write(data.into()))?;
33        Ok(())
34    }
35
36    pub fn set_color(&self, spec: &ColorSpec) -> Result<(), Error> {
38        self.send(Message::SetColor(spec.clone()))?;
39        Ok(())
40    }
41
42    pub fn reset(&self) -> Result<(), Error> {
44        self.send(Message::ResetStyle)?;
45        Ok(())
46    }
47
48    pub fn flush(&self) -> Result<(), Error> {
50        self.send(Message::Flush)?;
51
52        match self.inner.receiver.recv() {
53            Ok(Response::Flushed) => Ok(()),
54            msg => Err(Error::worker_error(format!("unexpected message {:?}", msg))),
55        }
56    }
57
58    fn send(&self, msg: Message) -> Result<(), Error> {
59        self.inner.sender.send(msg)?;
60        Ok(())
61    }
62}
63
64use crossbeam_channel as channel;
65use std::thread;
66
67struct InternalFormatter {
68    sender: channel::Sender<Message>,
69    receiver: channel::Receiver<Response>,
70    worker: Option<thread::JoinHandle<()>>,
72}
73
74impl InternalFormatter {
75    pub(crate) fn init_with<W: WriteColor, F: FnOnce() -> Result<W, Error> + Send + 'static>(
76        init: F,
77    ) -> Result<Self, Error> {
78        let (message_sender, message_receiver) = channel::unbounded();
79        let (response_sender, response_receiver) = channel::bounded(0);
80
81        let worker = thread::spawn(move || {
82            let mut buffer = match init() {
83                Ok(buf) => {
84                    let _ = response_sender.send(Response::StartedSuccessfully);
85                    buf
86                }
87                Err(e) => {
88                    let _ = response_sender.send(Response::Error(e));
89                    return;
90                }
91            };
92
93            macro_rules! maybe_log_error {
94                () => {
95                    |e| {
96                        if cfg!(debug_assertions) {
97                            eprintln!("{}", e)
98                        } else {
99                            ()
100                        }
101                    }
102                };
103            }
104
105            loop {
106                match message_receiver.recv() {
107                    Ok(Message::Write(data)) => {
108                        let _ = buffer.write_all(&data).map_err(maybe_log_error!());
109                    }
110                    Ok(Message::SetColor(data)) => {
111                        let _ = buffer.set_color(&data).map_err(maybe_log_error!());
112                    }
113                    Ok(Message::ResetStyle) => {
114                        let _ = buffer.reset().map_err(maybe_log_error!());
115                    }
116                    Ok(Message::Flush) => {
117                        let _ = buffer.flush().map_err(maybe_log_error!());
118                        let _ = response_sender.send(Response::Flushed);
119                    }
120                    Ok(Message::Exit) | Err(_) => {
121                        break;
122                    }
123                };
124            }
125        });
126
127        match response_receiver.recv() {
128            Ok(Response::Error(error)) => Err(error),
129            Ok(Response::StartedSuccessfully) => Ok(InternalFormatter {
130                worker: Some(worker),
131                sender: message_sender,
132                receiver: response_receiver,
133            }),
134            msg => Err(Error::worker_error(format!("unexpected message {:?}", msg))),
135        }
136    }
137}
138
139impl Drop for InternalFormatter {
140    fn drop(&mut self) {
141        let _ = self.sender.send(Message::Exit);
142        if let Some(worker) = self.worker.take() {
144            let _ = worker.join();
145        }
146    }
147}
148
149#[derive(Debug)]
150enum Message {
151    Write(Vec<u8>),
152    SetColor(ColorSpec),
153    ResetStyle,
154    Flush,
155    Exit,
156}
157
158#[derive(Debug)]
159enum Response {
160    StartedSuccessfully,
161    Error(Error),
162    Flushed,
163}
164
165#[macro_export]
201macro_rules! render_for_humans {
202    ($this:ident -> []) => {
203        fn render_for_humans(&self, fmt: &mut $crate::human::Formatter) -> Result<(), $crate::Error> {
204            Ok(())
205        }
206    };
207    ($self:ident -> [$($item:expr,)*]) => {
208        fn render_for_humans(&$self, fmt: &mut $crate::human::Formatter) -> Result<(), $crate::Error> {
209            let span = $crate::span!([ $( $item, )* ]);
210            span.render_for_humans(fmt)?;
211            Ok(())
212        }
213    };
214}
215
216mod test_helper {
217    use super::Formatter;
218    use crate::{test_buffer::TestBuffer, Target};
219    use termcolor::Buffer;
220
221    pub fn test() -> TestTarget {
245        TestTarget {
246            buffer: Buffer::no_color().into(),
247        }
248    }
249
250    pub fn test_with_color() -> TestTarget {
254        TestTarget {
255            buffer: Buffer::ansi().into(),
256        }
257    }
258
259    pub struct TestTarget {
260        buffer: TestBuffer,
261    }
262
263    impl TestTarget {
264        pub fn formatter(&self) -> Formatter {
265            let buffer = self.buffer.clone();
266            Formatter::init_with(|| Ok(buffer)).unwrap()
267        }
268
269        pub fn target(&self) -> Target {
270            Target::human(self.formatter())
271        }
272
273        pub fn to_string(&self) -> String {
274            let target = self.buffer.0.clone();
275            let buffer = target.read().unwrap();
276            String::from_utf8_lossy(buffer.as_slice()).to_string()
277        }
278    }
279}