1use std::{
2 io::{self, Write},
3 pin::Pin,
4 sync::{Arc, Mutex},
5 task::{Context, Poll},
6};
7use tokio::io::AsyncWrite;
8
9#[derive(Clone)]
11pub struct LogEndpoint {
12 name: Vec<u8>,
13 writer: Arc<Mutex<dyn Write + Send>>,
14}
15
16impl LogEndpoint {
17 pub fn new(name: &[u8], writer: Arc<Mutex<dyn Write + Send>>) -> LogEndpoint {
20 LogEndpoint {
21 name: name.to_owned(),
22 writer,
23 }
24 }
25
26 pub fn write_entry(&self, mut msg: &[u8]) -> io::Result<()> {
33 const LOG_ENDPOINT_DELIM: &[u8] = b" :: ";
34
35 if msg.last() == Some(&b'\n') {
38 msg = &msg[..msg.len() - 1];
39 }
40
41 if msg.is_empty() {
42 return Ok(());
43 }
44
45 let mut to_write =
47 Vec::with_capacity(msg.len() + self.name.len() + LOG_ENDPOINT_DELIM.len() + 1);
48
49 to_write.extend_from_slice(&self.name);
50 to_write.extend_from_slice(LOG_ENDPOINT_DELIM);
51 for &byte in msg {
52 if byte == b'\n' {
53 to_write.extend_from_slice(br"\n");
54 } else {
55 to_write.push(byte);
56 }
57 }
58 to_write.push(b'\n');
59
60 self.writer.lock().unwrap().write_all(&to_write)
61 }
62}
63
64impl Write for LogEndpoint {
65 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
66 self.write_entry(buf)?;
67 Ok(buf.len())
68 }
69
70 fn flush(&mut self) -> io::Result<()> {
71 self.writer.lock().unwrap().flush()
72 }
73}
74
75impl wasmtime_wasi::cli::StdoutStream for LogEndpoint {
76 fn p2_stream(&self) -> Box<dyn wasmtime_wasi::p2::OutputStream> {
77 Box::new(self.clone())
78 }
79
80 fn async_stream(&self) -> Box<dyn AsyncWrite + Send + Sync> {
81 Box::new(self.clone())
82 }
83}
84
85#[wiggle::async_trait]
86impl wasmtime_wasi::p2::Pollable for LogEndpoint {
87 async fn ready(&mut self) {}
88}
89
90impl wasmtime_wasi::cli::IsTerminal for LogEndpoint {
91 fn is_terminal(&self) -> bool {
92 false
93 }
94}
95
96impl wasmtime_wasi::p2::OutputStream for LogEndpoint {
97 fn write(&mut self, bytes: bytes::Bytes) -> wasmtime_wasi::p2::StreamResult<()> {
98 self.write_entry(&bytes)
99 .map_err(|e| wasmtime_wasi::p2::StreamError::LastOperationFailed(anyhow::anyhow!(e)))
100 }
101
102 fn flush(&mut self) -> wasmtime_wasi::p2::StreamResult<()> {
103 <Self as Write>::flush(self)
104 .map_err(|e| wasmtime_wasi::p2::StreamError::LastOperationFailed(anyhow::anyhow!(e)))
105 }
106
107 fn check_write(&mut self) -> wasmtime_wasi::p2::StreamResult<usize> {
108 Ok(1024 * 1024)
109 }
110}
111
112impl AsyncWrite for LogEndpoint {
113 fn poll_write(
114 self: Pin<&mut Self>,
115 _cx: &mut Context<'_>,
116 buf: &[u8],
117 ) -> Poll<Result<usize, std::io::Error>> {
118 self.write_entry(&buf)?;
119 Poll::Ready(Ok(buf.len()))
120 }
121
122 fn poll_flush(
123 mut self: Pin<&mut Self>,
124 _cx: &mut Context<'_>,
125 ) -> Poll<Result<(), std::io::Error>> {
126 Poll::Ready(<Self as Write>::flush(&mut self))
127 }
128
129 fn poll_shutdown(
130 self: Pin<&mut Self>,
131 _cx: &mut Context<'_>,
132 ) -> Poll<Result<(), std::io::Error>> {
133 Poll::Ready(Ok(()))
134 }
135}