//! Shares process stdio across stores without a parent-side IO pump. use super::bindings::sausage::process::host::Stdio; use std::{ pin::Pin, sync::{Arc, Mutex}, task::{Context, Poll}, }; use tokio::io::AsyncWrite; use wasmtime_wasi::cli::{AsyncStdinStream, StdinStream}; #[derive(Clone)] pub(super) struct Io { pub input: Arc, pub output: Writer, pub error: Writer, } impl Io { pub fn input(&self) -> sausage_vfs::preview1::Input { Box::new(Pin::from(self.input.async_stream())) } } #[derive(Clone)] pub(super) struct Writer(Arc>>); impl Writer { pub fn new(writer: impl AsyncWrite + Send + Sync + Unpin + 'static) -> Self { Self(Arc::new(Mutex::new(Box::new(writer)))) } } impl AsyncWrite for Writer { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, bytes: &[u8], ) -> Poll> { Pin::new(&mut **self.0.lock().unwrap()).poll_write(cx, bytes) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Pin::new(&mut **self.0.lock().unwrap()).poll_flush(cx) } fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { // Closing one inherited handle must not close the other handles. self.poll_flush(cx) } } pub(super) fn input( mode: Stdio, parent: Option<&Io>, ) -> (Option, Arc) { match mode { Stdio::Pipe => { let (host, guest) = tokio::io::duplex(8192); (Some(host), Arc::new(AsyncStdinStream::new(guest))) } Stdio::Inherit => ( None, parent.expect("inherit requires a parent").input.clone(), ), Stdio::Null => (None, Arc::new(AsyncStdinStream::new(tokio::io::empty()))), } } pub(super) fn output( mode: Stdio, parent: Option<&Writer>, ) -> (Option, Writer) { match mode { Stdio::Pipe => { let (host, guest) = tokio::io::duplex(8192); (Some(host), Writer::new(guest)) } Stdio::Inherit => (None, parent.expect("inherit requires a parent").clone()), Stdio::Null => (None, Writer::new(tokio::io::sink())), } }