Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: If8fe8b38c1d9c4fecd40ff71f88d2ae06a6a6964
196 lines
4.9 KiB
Rust
196 lines
4.9 KiB
Rust
//! WASM bridge types and helpers for plugin communication
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
/// Memory allocation info for passing data between host and plugin
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct WasmMemoryAlloc {
|
|
/// Pointer to allocated memory
|
|
pub ptr: u32,
|
|
|
|
/// Size of allocation in bytes
|
|
pub len: u32,
|
|
}
|
|
|
|
/// Request from host to plugin
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct HostRequest {
|
|
/// Request ID for matching with response
|
|
pub request_id: String,
|
|
|
|
/// Method name being called
|
|
pub method: String,
|
|
|
|
/// Serialized parameters
|
|
pub params: Vec<u8>,
|
|
}
|
|
|
|
/// Response from plugin to host
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct PluginResponse {
|
|
/// Request ID this response corresponds to
|
|
pub request_id: String,
|
|
|
|
/// Success or error
|
|
pub result: WasmResult<Vec<u8>>,
|
|
}
|
|
|
|
/// Result type for WASM operations
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub enum WasmResult<T> {
|
|
Ok(T),
|
|
Err(String),
|
|
}
|
|
|
|
impl<T> From<Result<T, String>> for WasmResult<T> {
|
|
fn from(r: Result<T, String>) -> Self {
|
|
match r {
|
|
Ok(v) => WasmResult::Ok(v),
|
|
Err(e) => WasmResult::Err(e),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Host functions available to plugins
|
|
pub mod host_functions {
|
|
/// Log a message from plugin
|
|
pub const LOG: &str = "host_log";
|
|
|
|
/// Read a file (if permitted)
|
|
pub const READ_FILE: &str = "host_read_file";
|
|
|
|
/// Write a file (if permitted)
|
|
pub const WRITE_FILE: &str = "host_write_file";
|
|
|
|
/// Make an HTTP request (if permitted)
|
|
pub const HTTP_REQUEST: &str = "host_http_request";
|
|
|
|
/// Get configuration value
|
|
pub const GET_CONFIG: &str = "host_get_config";
|
|
|
|
/// Emit an event
|
|
pub const EMIT_EVENT: &str = "host_emit_event";
|
|
}
|
|
|
|
/// Log level for plugin logging
|
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
|
pub enum LogLevel {
|
|
Trace,
|
|
Debug,
|
|
Info,
|
|
Warn,
|
|
Error,
|
|
}
|
|
|
|
/// Log message from plugin
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct LogMessage {
|
|
pub level: LogLevel,
|
|
pub target: String,
|
|
pub message: String,
|
|
pub fields: HashMap<String, String>,
|
|
}
|
|
|
|
/// HTTP request parameters
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct HttpRequest {
|
|
pub method: String,
|
|
pub url: String,
|
|
pub headers: HashMap<String, String>,
|
|
pub body: Option<Vec<u8>>,
|
|
}
|
|
|
|
/// HTTP response
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct HttpResponse {
|
|
pub status: u16,
|
|
pub headers: HashMap<String, String>,
|
|
pub body: Vec<u8>,
|
|
}
|
|
|
|
/// Helper functions for serializing/deserializing data across WASM boundary
|
|
pub mod helpers {
|
|
use super::*;
|
|
|
|
/// Serialize a value to bytes for passing to WASM
|
|
pub fn serialize<T: Serialize>(value: &T) -> Result<Vec<u8>, String> {
|
|
serde_json::to_vec(value).map_err(|e| format!("Serialization error: {}", e))
|
|
}
|
|
|
|
/// Deserialize bytes from WASM to a value
|
|
pub fn deserialize<T: for<'de> Deserialize<'de>>(
|
|
bytes: &[u8],
|
|
) -> Result<T, String> {
|
|
serde_json::from_slice(bytes)
|
|
.map_err(|e| format!("Deserialization error: {}", e))
|
|
}
|
|
|
|
/// Create a success response
|
|
pub fn ok_response<T: Serialize>(
|
|
request_id: String,
|
|
value: &T,
|
|
) -> Result<Vec<u8>, String> {
|
|
let result = WasmResult::Ok(serialize(value)?);
|
|
let response = PluginResponse { request_id, result };
|
|
serialize(&response)
|
|
}
|
|
|
|
/// Create an error response
|
|
pub fn error_response(
|
|
request_id: String,
|
|
error: String,
|
|
) -> Result<Vec<u8>, String> {
|
|
let result = WasmResult::<Vec<u8>>::Err(error);
|
|
let response = PluginResponse { request_id, result };
|
|
serialize(&response)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::{helpers::*, *};
|
|
|
|
#[test]
|
|
fn test_serialize_deserialize() {
|
|
let data = vec![1u8, 2, 3, 4];
|
|
let bytes = serialize(&data).unwrap();
|
|
let recovered: Vec<u8> = deserialize(&bytes).unwrap();
|
|
assert_eq!(data, recovered);
|
|
}
|
|
|
|
#[test]
|
|
fn test_ok_response() {
|
|
let request_id = "test-123".to_string();
|
|
let value = "success";
|
|
let response_bytes = ok_response(request_id.clone(), &value).unwrap();
|
|
|
|
let response: PluginResponse = deserialize(&response_bytes).unwrap();
|
|
assert_eq!(response.request_id, request_id);
|
|
|
|
match response.result {
|
|
WasmResult::Ok(data) => {
|
|
let recovered: String = deserialize(&data).unwrap();
|
|
assert_eq!(recovered, value);
|
|
},
|
|
WasmResult::Err(_) => panic!("Expected Ok result"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_error_response() {
|
|
let request_id = "test-456".to_string();
|
|
let error_msg = "Something went wrong";
|
|
let response_bytes =
|
|
error_response(request_id.clone(), error_msg.to_string()).unwrap();
|
|
|
|
let response: PluginResponse = deserialize(&response_bytes).unwrap();
|
|
assert_eq!(response.request_id, request_id);
|
|
|
|
match response.result {
|
|
WasmResult::Err(msg) => assert_eq!(msg, error_msg),
|
|
WasmResult::Ok(_) => panic!("Expected Err result"),
|
|
}
|
|
}
|
|
}
|