mod handler;
pub use handler::{ReplayHandler, ReplayState};
use crate::pack_bridge::{ConversionError, IntoValue};
use packr::abi::Value;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HostFunctionCall {
pub interface: String,
pub function: String,
pub input: Value,
pub output: Value,
}
impl HostFunctionCall {
pub fn new(
interface: impl Into<String>,
function: impl Into<String>,
input: Value,
output: Value,
) -> Self {
Self {
interface: interface.into(),
function: function.into(),
input,
output,
}
}
pub fn no_input(
interface: impl Into<String>,
function: impl Into<String>,
output: Value,
) -> Self {
Self::new(interface, function, Value::Tuple(vec![]), output)
}
}
impl IntoValue for HostFunctionCall {
fn into_value(self) -> Value {
Value::Record {
type_name: String::from("host-function-call"),
fields: vec![
("interface".into(), Value::String(self.interface)),
("function".into(), Value::String(self.function)),
("input".into(), self.input),
("output".into(), self.output),
],
}
}
}
impl TryFrom<Value> for HostFunctionCall {
type Error = ConversionError;
fn try_from(v: Value) -> Result<Self, Self::Error> {
match v {
Value::Record { fields, .. } => {
let mut interface = String::new();
let mut function = String::new();
let mut input = Value::Tuple(vec![]);
let mut output = Value::Tuple(vec![]);
for (name, val) in fields {
match name.as_str() {
"interface" => interface = String::try_from(val)?,
"function" => function = String::try_from(val)?,
"input" => input = val,
"output" => output = val,
_ => {}
}
}
Ok(HostFunctionCall {
interface,
function,
input,
output,
})
}
other => Err(ConversionError::ExpectedRecord(format!("{:?}", other))),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_host_function_call_serialization() {
let call = HostFunctionCall::new(
"wasi:clocks/wall-clock@0.2.3",
"now",
Value::Tuple(vec![]),
Value::U64(1234567890),
);
let json = serde_json::to_string(&call).unwrap();
let parsed: HostFunctionCall = serde_json::from_str(&json).unwrap();
assert_eq!(call.interface, parsed.interface);
assert_eq!(call.function, parsed.function);
assert_eq!(call.input, parsed.input);
assert_eq!(call.output, parsed.output);
}
#[test]
fn test_type_preserved_in_json() {
let call = HostFunctionCall::new(
"wasi:random/random@0.2.3",
"get-random-u64",
Value::Tuple(vec![]),
Value::U64(42),
);
let json = serde_json::to_string(&call).unwrap();
assert!(json.contains("U64"), "Type tag should be in JSON: {}", json);
}
#[test]
fn test_no_input() {
let call =
HostFunctionCall::no_input("wasi:clocks/wall-clock@0.2.3", "now", Value::U64(999));
assert_eq!(call.input, Value::Tuple(vec![]));
}
}