use bytes::Bytes;
use colored::*;
use prost::bytes;
use std::io;
use std::io::{IsTerminal, Write};
use uuid::Uuid;
use wasmtime::component::ResourceTable;
use wasmtime_wasi::async_trait;
use wasmtime_wasi::p2::{
IoView, OutputStream, Pollable, StdoutStream, StreamError, StreamResult, WasiCtx, WasiView,
};
use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView};
pub type Id = Uuid;
pub struct InstanceState {
id: Id,
arguments: Vec<String>,
wasi_ctx: WasiCtx,
resource_table: ResourceTable,
http_ctx: WasiHttpCtx,
}
impl IoView for InstanceState {
fn table(&mut self) -> &mut ResourceTable {
&mut self.resource_table
}
}
impl WasiView for InstanceState {
fn ctx(&mut self) -> &mut WasiCtx {
&mut self.wasi_ctx
}
}
impl WasiHttpView for InstanceState {
fn ctx(&mut self) -> &mut WasiHttpCtx {
&mut self.http_ctx
}
}
impl InstanceState {
pub async fn new(id: Uuid, arguments: Vec<String>) -> Self {
let mut builder = WasiCtx::builder();
builder.inherit_network(); builder.stdout(Stdout(shorten_uuid(&id)));
builder.stderr(Stderr(shorten_uuid(&id)));
InstanceState {
id,
arguments,
wasi_ctx: builder.build(),
resource_table: ResourceTable::new(),
http_ctx: WasiHttpCtx::new(),
}
}
pub fn id(&self) -> Id {
self.id
}
pub fn arguments(&self) -> &[String] {
&self.arguments
}
}
fn shorten_uuid(uuid: &Uuid) -> String {
uuid.to_string().split('-').next().unwrap().to_string()
}
struct Stdout(String);
struct Stderr(String);
impl StdoutStream for Stdout {
fn stream(&self) -> Box<dyn OutputStream> {
Box::new(StdioOutputStream::Stdout(self.0.to_string()))
}
fn isatty(&self) -> bool {
io::stderr().is_terminal()
}
}
impl StdoutStream for Stderr {
fn stream(&self) -> Box<dyn OutputStream> {
Box::new(StdioOutputStream::Stderr(self.0.to_string()))
}
fn isatty(&self) -> bool {
io::stderr().is_terminal()
}
}
enum StdioOutputStream {
Stdout(String),
Stderr(String),
}
impl OutputStream for StdioOutputStream {
fn write(&mut self, bytes: Bytes) -> StreamResult<()> {
let message = String::from_utf8_lossy(&bytes);
let colored_message = match self {
StdioOutputStream::Stdout(prefix) => {
format!("[Inst {}] {}", prefix, message).green().to_string()
}
StdioOutputStream::Stderr(prefix) => {
format!("[Inst {}] {}", prefix, message).red().to_string()
}
};
match self {
StdioOutputStream::Stdout(_) => io::stdout().write_all(colored_message.as_bytes()),
StdioOutputStream::Stderr(_) => io::stderr().write_all(colored_message.as_bytes()),
}
.map_err(|e| StreamError::LastOperationFailed(anyhow::anyhow!(e)))
}
fn flush(&mut self) -> StreamResult<()> {
match self {
StdioOutputStream::Stdout(_) => io::stdout().flush(),
StdioOutputStream::Stderr(_) => io::stderr().flush(),
}
.map_err(|e| StreamError::LastOperationFailed(anyhow::anyhow!(e)))
}
fn check_write(&mut self) -> StreamResult<usize> {
Ok(1024 * 1024)
}
}
#[async_trait]
impl Pollable for StdioOutputStream {
async fn ready(&mut self) {}
}