use std::io::{self, Write};
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::JsCast;
use wasm_bindgen::prelude::*;
#[cfg_attr(not(target_arch = "wasm32"), derive(Default))]
pub struct WebOutputWriter {
#[cfg(target_arch = "wasm32")]
callback: js_sys::Function,
}
#[cfg(target_arch = "wasm32")]
#[allow(unsafe_code)]
unsafe impl Send for WebOutputWriter {}
impl WebOutputWriter {
#[cfg(target_arch = "wasm32")]
pub fn try_new(callback: js_sys::Function) -> Result<Self, JsValue> {
let memory: js_sys::WebAssembly::Memory = wasm_bindgen::memory().unchecked_into();
if memory
.buffer()
.is_instance_of::<js_sys::SharedArrayBuffer>()
{
return Err(JsValue::from_str(
"plushie-renderer-wasm does not support shared-memory wasm builds; \
the JavaScript callback output path is not thread-safe",
));
}
Ok(Self { callback })
}
#[cfg(not(target_arch = "wasm32"))]
pub fn try_new(_callback: js_sys::Function) -> Result<Self, JsValue> {
Ok(Self {})
}
}
impl Write for WebOutputWriter {
#[cfg(target_arch = "wasm32")]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let json = String::from_utf8_lossy(buf);
let js_str = JsValue::from_str(&json);
self.callback.call1(&JsValue::NULL, &js_str).map_err(|e| {
let msg = e
.as_string()
.unwrap_or_else(|| "unknown JS error".to_string());
io::Error::other(format!("JS callback failed: {msg}"))
})?;
Ok(buf.len())
}
#[cfg(not(target_arch = "wasm32"))]
fn write(&mut self, _buf: &[u8]) -> io::Result<usize> {
Err(io::Error::other("WebOutputWriter is only usable on wasm32"))
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}