use core::fmt;
use tokio::io::{
empty, stderr, stdin, stdout, AsyncRead, AsyncWrite, Empty, Stderr, Stdin, Stdout,
};
use wasmtime::component::Linker;
use crate::Ctx;
mod host;
pub struct WasiCliCtx {
pub environment: Vec<(String, String)>,
pub arguments: Vec<String>,
pub initial_cwd: Option<String>,
pub stdin: Box<dyn InputStream + Send>,
pub stdout: Box<dyn OutputStream + Send>,
pub stderr: Box<dyn OutputStream + Send>,
}
impl Default for WasiCliCtx {
fn default() -> Self {
Self {
environment: Vec::default(),
arguments: Vec::default(),
initial_cwd: None,
stdin: Box::new(empty()),
stdout: Box::new(empty()),
stderr: Box::new(empty()),
}
}
}
pub fn add_to_linker<T: Send>(
linker: &mut Linker<T>,
get: impl Fn(&mut T) -> &mut Ctx + Copy + Sync + Send + 'static,
) -> wasmtime::Result<()> {
let exit_options = crate::engine::bindings::wasi::cli::exit::LinkOptions::default();
add_to_linker_with_options(linker, get, &exit_options)
}
pub fn add_to_linker_with_options<T: Send>(
linker: &mut Linker<T>,
get: impl Fn(&mut T) -> &mut Ctx + Copy + Sync + Send + 'static,
exit_options: &crate::engine::bindings::wasi::cli::exit::LinkOptions,
) -> anyhow::Result<()> {
crate::engine::bindings::wasi::cli::environment::add_to_linker(linker, get)?;
crate::engine::bindings::wasi::cli::exit::add_to_linker(linker, exit_options, get)?;
crate::engine::bindings::wasi::cli::stdin::add_to_linker(linker, get)?;
crate::engine::bindings::wasi::cli::stdout::add_to_linker(linker, get)?;
crate::engine::bindings::wasi::cli::stderr::add_to_linker(linker, get)?;
crate::engine::bindings::wasi::cli::terminal_input::add_to_linker(linker, get)?;
crate::engine::bindings::wasi::cli::terminal_output::add_to_linker(linker, get)?;
crate::engine::bindings::wasi::cli::terminal_stdin::add_to_linker(linker, get)?;
crate::engine::bindings::wasi::cli::terminal_stdout::add_to_linker(linker, get)?;
crate::engine::bindings::wasi::cli::terminal_stderr::add_to_linker(linker, get)?;
Ok(())
}
#[derive(Debug)]
pub struct I32Exit(pub i32);
impl fmt::Display for I32Exit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Exited with i32 exit status {}", self.0)
}
}
impl std::error::Error for I32Exit {}
pub struct TerminalInput;
pub struct TerminalOutput;
pub trait IsTerminal {
fn is_terminal(&self) -> bool;
}
impl IsTerminal for Empty {
fn is_terminal(&self) -> bool {
false
}
}
pub trait InputStream: IsTerminal {
fn reader(&self) -> Box<dyn AsyncRead + Send + Sync + Unpin>;
}
pub trait OutputStream: IsTerminal {
fn writer(&self) -> Box<dyn AsyncWrite + Send + Sync + Unpin>;
}
impl InputStream for Empty {
fn reader(&self) -> Box<dyn AsyncRead + Send + Sync + Unpin> {
Box::new(empty())
}
}
impl OutputStream for Empty {
fn writer(&self) -> Box<dyn AsyncWrite + Send + Sync + Unpin> {
Box::new(empty())
}
}
impl IsTerminal for std::io::Empty {
fn is_terminal(&self) -> bool {
false
}
}
impl InputStream for std::io::Empty {
fn reader(&self) -> Box<dyn AsyncRead + Send + Sync + Unpin> {
Box::new(empty())
}
}
impl OutputStream for std::io::Empty {
fn writer(&self) -> Box<dyn AsyncWrite + Send + Sync + Unpin> {
Box::new(empty())
}
}
impl IsTerminal for Stdin {
fn is_terminal(&self) -> bool {
std::io::stdin().is_terminal()
}
}
impl InputStream for Stdin {
fn reader(&self) -> Box<dyn AsyncRead + Send + Sync + Unpin> {
Box::new(stdin())
}
}
impl IsTerminal for std::io::Stdin {
fn is_terminal(&self) -> bool {
std::io::IsTerminal::is_terminal(self)
}
}
impl InputStream for std::io::Stdin {
fn reader(&self) -> Box<dyn AsyncRead + Send + Sync + Unpin> {
Box::new(stdin())
}
}
impl IsTerminal for Stdout {
fn is_terminal(&self) -> bool {
std::io::stdout().is_terminal()
}
}
impl OutputStream for Stdout {
fn writer(&self) -> Box<dyn AsyncWrite + Send + Sync + Unpin> {
Box::new(stdout())
}
}
impl IsTerminal for std::io::Stdout {
fn is_terminal(&self) -> bool {
std::io::IsTerminal::is_terminal(self)
}
}
impl OutputStream for std::io::Stdout {
fn writer(&self) -> Box<dyn AsyncWrite + Send + Sync + Unpin> {
Box::new(stdout())
}
}
impl IsTerminal for Stderr {
fn is_terminal(&self) -> bool {
std::io::stderr().is_terminal()
}
}
impl OutputStream for Stderr {
fn writer(&self) -> Box<dyn AsyncWrite + Send + Sync + Unpin> {
Box::new(stderr())
}
}
impl IsTerminal for std::io::Stderr {
fn is_terminal(&self) -> bool {
std::io::IsTerminal::is_terminal(self)
}
}
impl OutputStream for std::io::Stderr {
fn writer(&self) -> Box<dyn AsyncWrite + Send + Sync + Unpin> {
Box::new(stderr())
}
}