use std::{fmt::Display, mem, pin::pin, task::Poll}; use futures::ready; use pin_project::pin_project; pub use tokio::io::AsyncWrite; #[pin_project] #[derive(Debug)] pub struct Loggable { log_buffer: Vec, #[pin] writer: W, } impl Loggable { pub fn new(writer: W) -> Self { Self { log_buffer: Vec::new(), writer, } } pub fn into_inner(self) -> W { self.writer } pub fn take_log(&mut self) -> Vec { let log: Vec = mem::replace(&mut self.log_buffer, Vec::new()); log } } impl Display for Loggable { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let str = str::from_utf8(&self.log_buffer).unwrap_or("buffer to string conversion failed"); f.write_str(str) } } impl AsyncWrite for Loggable { fn poll_write( mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, buf: &[u8], ) -> std::task::Poll> { let this = self.as_mut().project(); let ready = ready!(this.writer.poll_write(cx, buf)); self.log_buffer.extend_from_slice(buf); Poll::Ready(ready) } fn poll_flush( self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { let this = self.project(); Poll::Ready(ready!(this.writer.poll_flush(cx))) } fn poll_shutdown( self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { let this = self.project(); Poll::Ready(ready!(this.writer.poll_shutdown(cx))) } }