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}