Skip to main content

wavecraft_protocol/ipc/
envelope.rs

1use serde::{Deserialize, Serialize};
2
3use super::IpcError;
4
5/// Request message sent from UI to Rust
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct IpcRequest {
8    /// JSON-RPC version (always "2.0")
9    pub jsonrpc: String,
10    /// Unique request identifier for matching responses
11    pub id: RequestId,
12    /// Method name to invoke
13    pub method: String,
14    /// Method parameters (method-specific)
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub params: Option<serde_json::Value>,
17}
18
19/// Response message sent from Rust to UI
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct IpcResponse {
22    /// JSON-RPC version (always "2.0")
23    pub jsonrpc: String,
24    /// Request ID this response corresponds to
25    pub id: RequestId,
26    /// Success result (mutually exclusive with error)
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub result: Option<serde_json::Value>,
29    /// Error result (mutually exclusive with result)
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub error: Option<IpcError>,
32}
33
34/// Notification message sent from Rust to UI (no response expected)
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct IpcNotification {
37    /// JSON-RPC version (always "2.0")
38    pub jsonrpc: String,
39    /// Event type
40    pub method: String,
41    /// Event data
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub params: Option<serde_json::Value>,
44}
45
46/// Request ID can be string or number
47#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
48#[serde(untagged)]
49pub enum RequestId {
50    String(String),
51    Number(i64),
52}
53
54impl IpcRequest {
55    /// Create a new request
56    pub fn new(
57        id: RequestId,
58        method: impl Into<String>,
59        params: Option<serde_json::Value>,
60    ) -> Self {
61        Self {
62            jsonrpc: "2.0".to_string(),
63            id,
64            method: method.into(),
65            params,
66        }
67    }
68}
69
70impl IpcResponse {
71    /// Try to create a success response.
72    pub fn try_success(id: RequestId, result: impl Serialize) -> serde_json::Result<Self> {
73        Ok(Self {
74            jsonrpc: "2.0".to_string(),
75            id,
76            result: Some(serde_json::to_value(result)?),
77            error: None,
78        })
79    }
80
81    /// Create a success response
82    pub fn success(id: RequestId, result: impl Serialize) -> Self {
83        let id_for_fallback = id.clone();
84        match Self::try_success(id, result) {
85            Ok(response) => response,
86            Err(err) => Self::error(
87                id_for_fallback,
88                IpcError::internal_error(format!("Failed to serialize success response: {err}")),
89            ),
90        }
91    }
92
93    /// Create an error response
94    pub fn error(id: RequestId, error: IpcError) -> Self {
95        Self {
96            jsonrpc: "2.0".to_string(),
97            id,
98            result: None,
99            error: Some(error),
100        }
101    }
102}
103
104impl IpcNotification {
105    /// Try to create a new notification.
106    pub fn try_new(method: impl Into<String>, params: impl Serialize) -> serde_json::Result<Self> {
107        Ok(Self {
108            jsonrpc: "2.0".to_string(),
109            method: method.into(),
110            params: Some(serde_json::to_value(params)?),
111        })
112    }
113
114    /// Create a new notification
115    pub fn new(method: impl Into<String>, params: impl Serialize) -> Self {
116        let method = method.into();
117        match Self::try_new(method.clone(), params) {
118            Ok(notification) => notification,
119            Err(_) => Self {
120                jsonrpc: "2.0".to_string(),
121                method,
122                params: None,
123            },
124        }
125    }
126}