Skip to main content

openclaw_ipc/
messages.rs

1//! IPC message types.
2
3use serde::{Deserialize, Serialize};
4
5/// IPC message envelope.
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct IpcMessage {
8    /// Message ID for request/response correlation.
9    pub id: String,
10    /// Message payload.
11    pub payload: IpcPayload,
12}
13
14/// IPC message payload.
15#[derive(Debug, Clone, Serialize, Deserialize)]
16#[serde(tag = "type", rename_all = "snake_case")]
17pub enum IpcPayload {
18    /// Request message.
19    Request(IpcRequest),
20    /// Response message.
21    Response(IpcResponse),
22    /// Event notification.
23    Event(IpcEvent),
24}
25
26/// IPC request.
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct IpcRequest {
29    /// Method name.
30    pub method: String,
31    /// Request parameters.
32    pub params: serde_json::Value,
33}
34
35/// IPC response.
36#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct IpcResponse {
38    /// Whether the request succeeded.
39    pub success: bool,
40    /// Result data (if success).
41    pub result: Option<serde_json::Value>,
42    /// Error message (if failure).
43    pub error: Option<String>,
44}
45
46/// IPC event.
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct IpcEvent {
49    /// Event type.
50    pub event_type: String,
51    /// Event data.
52    pub data: serde_json::Value,
53}
54
55impl IpcMessage {
56    /// Create a new request message.
57    #[must_use]
58    pub fn request(method: impl Into<String>, params: serde_json::Value) -> Self {
59        Self {
60            id: uuid_v4(),
61            payload: IpcPayload::Request(IpcRequest {
62                method: method.into(),
63                params,
64            }),
65        }
66    }
67
68    /// Create a success response.
69    #[must_use]
70    pub fn success(id: impl Into<String>, result: serde_json::Value) -> Self {
71        Self {
72            id: id.into(),
73            payload: IpcPayload::Response(IpcResponse {
74                success: true,
75                result: Some(result),
76                error: None,
77            }),
78        }
79    }
80
81    /// Create an error response.
82    #[must_use]
83    pub fn error(id: impl Into<String>, error: impl Into<String>) -> Self {
84        Self {
85            id: id.into(),
86            payload: IpcPayload::Response(IpcResponse {
87                success: false,
88                result: None,
89                error: Some(error.into()),
90            }),
91        }
92    }
93
94    /// Create an event message.
95    #[must_use]
96    pub fn event(event_type: impl Into<String>, data: serde_json::Value) -> Self {
97        Self {
98            id: uuid_v4(),
99            payload: IpcPayload::Event(IpcEvent {
100                event_type: event_type.into(),
101                data,
102            }),
103        }
104    }
105}
106
107/// Generate a simple UUID v4.
108fn uuid_v4() -> String {
109    let bytes: [u8; 16] = rand::random();
110    format!(
111        "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
112        bytes[0],
113        bytes[1],
114        bytes[2],
115        bytes[3],
116        bytes[4],
117        bytes[5],
118        (bytes[6] & 0x0f) | 0x40,
119        bytes[7],
120        (bytes[8] & 0x3f) | 0x80,
121        bytes[9],
122        bytes[10],
123        bytes[11],
124        bytes[12],
125        bytes[13],
126        bytes[14],
127        bytes[15]
128    )
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    #[test]
136    fn test_request_message() {
137        let msg = IpcMessage::request("test.method", serde_json::json!({"key": "value"}));
138        assert!(!msg.id.is_empty());
139        if let IpcPayload::Request(req) = msg.payload {
140            assert_eq!(req.method, "test.method");
141        } else {
142            panic!("Expected request payload");
143        }
144    }
145
146    #[test]
147    fn test_success_response() {
148        let msg = IpcMessage::success("123", serde_json::json!({"result": true}));
149        if let IpcPayload::Response(resp) = msg.payload {
150            assert!(resp.success);
151            assert!(resp.result.is_some());
152        } else {
153            panic!("Expected response payload");
154        }
155    }
156}