use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IpcMessage {
pub id: String,
pub cmd: String,
pub payload: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IpcResponse {
pub id: String,
pub success: bool,
pub data: serde_json::Value,
}
pub type IpcResponseSender = Arc<dyn Fn(IpcResponse) + Send + Sync + 'static>;
pub trait IpcHandler: Send + Sync {
fn handle(&self, message: IpcMessage) -> IpcResponse;
fn handle_async(&self, message: IpcMessage, respond: IpcResponseSender) {
respond(self.handle(message));
}
}
pub struct FnIpcHandler<F>
where
F: Fn(IpcMessage) -> IpcResponse + Send + Sync,
{
handler: F,
}
impl<F> FnIpcHandler<F>
where
F: Fn(IpcMessage) -> IpcResponse + Send + Sync,
{
pub fn new(handler: F) -> Self {
Self { handler }
}
}
impl<F> IpcHandler for FnIpcHandler<F>
where
F: Fn(IpcMessage) -> IpcResponse + Send + Sync,
{
fn handle(&self, message: IpcMessage) -> IpcResponse {
(self.handler)(message)
}
}
#[cfg(test)]
mod tests {
use super::{FnIpcHandler, IpcHandler, IpcMessage, IpcResponse, IpcResponseSender};
use serde_json::json;
use std::sync::{Arc, Mutex};
#[test]
fn default_async_dispatch_preserves_sync_handler_contract() {
let handler = FnIpcHandler::new(|message: IpcMessage| IpcResponse {
id: message.id,
success: true,
data: json!({ "method": message.cmd }),
});
let received = Arc::new(Mutex::new(None));
let sink_target = received.clone();
let sink: IpcResponseSender = Arc::new(move |response| {
*sink_target.lock().unwrap() = Some(response);
});
handler.handle_async(
IpcMessage {
id: "async-1".to_string(),
cmd: "ping".to_string(),
payload: json!({}),
},
sink,
);
let response = received.lock().unwrap().take().unwrap();
assert_eq!(response.id, "async-1");
assert_eq!(response.data["method"], "ping");
}
}