use crate::preview2::bindings::cli::{
stderr, stdin, stdout, terminal_input, terminal_output, terminal_stderr, terminal_stdin,
terminal_stdout,
};
use crate::preview2::bindings::io::streams;
use crate::preview2::pipe;
use crate::preview2::{
HostInputStream, HostOutputStream, StreamError, StreamResult, Subscribe, WasiView,
};
use bytes::Bytes;
use std::io::IsTerminal;
use wasmtime::component::Resource;
pub trait StdinStream: Send {
fn stream(&self) -> Box<dyn HostInputStream>;
fn isatty(&self) -> bool;
}
impl StdinStream for pipe::MemoryInputPipe {
fn stream(&self) -> Box<dyn HostInputStream> {
Box::new(self.clone())
}
fn isatty(&self) -> bool {
false
}
}
impl StdinStream for pipe::ClosedInputStream {
fn stream(&self) -> Box<dyn HostInputStream> {
Box::new(self.clone())
}
fn isatty(&self) -> bool {
false
}
}
mod worker_thread_stdin;
pub use self::worker_thread_stdin::{stdin, Stdin};
pub trait StdoutStream: Send {
fn stream(&self) -> Box<dyn HostOutputStream>;
fn isatty(&self) -> bool;
}
impl StdoutStream for pipe::MemoryOutputPipe {
fn stream(&self) -> Box<dyn HostOutputStream> {
Box::new(self.clone())
}
fn isatty(&self) -> bool {
false
}
}
impl StdoutStream for pipe::SinkOutputStream {
fn stream(&self) -> Box<dyn HostOutputStream> {
Box::new(self.clone())
}
fn isatty(&self) -> bool {
false
}
}
impl StdoutStream for pipe::ClosedOutputStream {
fn stream(&self) -> Box<dyn HostOutputStream> {
Box::new(self.clone())
}
fn isatty(&self) -> bool {
false
}
}
pub struct Stdout;
pub fn stdout() -> Stdout {
Stdout
}
impl StdoutStream for Stdout {
fn stream(&self) -> Box<dyn HostOutputStream> {
Box::new(OutputStream::Stdout)
}
fn isatty(&self) -> bool {
std::io::stdout().is_terminal()
}
}
pub struct Stderr;
pub fn stderr() -> Stderr {
Stderr
}
impl StdoutStream for Stderr {
fn stream(&self) -> Box<dyn HostOutputStream> {
Box::new(OutputStream::Stderr)
}
fn isatty(&self) -> bool {
std::io::stderr().is_terminal()
}
}
enum OutputStream {
Stdout,
Stderr,
}
impl HostOutputStream for OutputStream {
fn write(&mut self, bytes: Bytes) -> StreamResult<()> {
use std::io::Write;
match self {
OutputStream::Stdout => std::io::stdout().write_all(&bytes),
OutputStream::Stderr => std::io::stderr().write_all(&bytes),
}
.map_err(|e| StreamError::LastOperationFailed(anyhow::anyhow!(e)))
}
fn flush(&mut self) -> StreamResult<()> {
use std::io::Write;
match self {
OutputStream::Stdout => std::io::stdout().flush(),
OutputStream::Stderr => std::io::stderr().flush(),
}
.map_err(|e| StreamError::LastOperationFailed(anyhow::anyhow!(e)))
}
fn check_write(&mut self) -> StreamResult<usize> {
Ok(1024 * 1024)
}
}
#[async_trait::async_trait]
impl Subscribe for OutputStream {
async fn ready(&mut self) {}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IsATTY {
Yes,
No,
}
impl<T: WasiView> stdin::Host for T {
fn get_stdin(&mut self) -> Result<Resource<streams::InputStream>, anyhow::Error> {
let stream = self.ctx().stdin.stream();
Ok(self.table().push(streams::InputStream::Host(stream))?)
}
}
impl<T: WasiView> stdout::Host for T {
fn get_stdout(&mut self) -> Result<Resource<streams::OutputStream>, anyhow::Error> {
let stream = self.ctx().stdout.stream();
Ok(self.table().push(stream)?)
}
}
impl<T: WasiView> stderr::Host for T {
fn get_stderr(&mut self) -> Result<Resource<streams::OutputStream>, anyhow::Error> {
let stream = self.ctx().stderr.stream();
Ok(self.table().push(stream)?)
}
}
pub struct TerminalInput;
pub struct TerminalOutput;
impl<T: WasiView> terminal_input::Host for T {}
impl<T: WasiView> terminal_input::HostTerminalInput for T {
fn drop(&mut self, r: Resource<TerminalInput>) -> anyhow::Result<()> {
self.table().delete(r)?;
Ok(())
}
}
impl<T: WasiView> terminal_output::Host for T {}
impl<T: WasiView> terminal_output::HostTerminalOutput for T {
fn drop(&mut self, r: Resource<TerminalOutput>) -> anyhow::Result<()> {
self.table().delete(r)?;
Ok(())
}
}
impl<T: WasiView> terminal_stdin::Host for T {
fn get_terminal_stdin(&mut self) -> anyhow::Result<Option<Resource<TerminalInput>>> {
if self.ctx().stdin.isatty() {
let fd = self.table().push(TerminalInput)?;
Ok(Some(fd))
} else {
Ok(None)
}
}
}
impl<T: WasiView> terminal_stdout::Host for T {
fn get_terminal_stdout(&mut self) -> anyhow::Result<Option<Resource<TerminalOutput>>> {
if self.ctx().stdout.isatty() {
let fd = self.table().push(TerminalOutput)?;
Ok(Some(fd))
} else {
Ok(None)
}
}
}
impl<T: WasiView> terminal_stderr::Host for T {
fn get_terminal_stderr(&mut self) -> anyhow::Result<Option<Resource<TerminalOutput>>> {
if self.ctx().stderr.isatty() {
let fd = self.table().push(TerminalOutput)?;
Ok(Some(fd))
} else {
Ok(None)
}
}
}