Skip to main content

opencore_jsonrpc_rust/
protocol.rs

1//! JSON-RPC protocol types for request/response communication.
2//!
3//! This module defines the core protocol structures used for communication
4//! between the TypeScript framework and Rust applications.
5
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9/// A JSON-RPC request from the client.
10///
11/// # Fields
12///
13/// * `id` - Unique identifier for the request, used to match responses
14/// * `action` - The name of the action/method to invoke
15/// * `params` - Array of JSON values representing the parameters
16///
17/// # Example JSON
18///
19/// ```json
20/// {
21///   "id": "req-123",
22///   "action": "sum",
23///   "params": [5, 10]
24/// }
25/// ```
26#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
27pub struct Request {
28    pub id: String,
29    pub action: String,
30    pub params: Vec<Value>,
31}
32
33/// A JSON-RPC response to the client.
34///
35/// Responses are tagged with a `status` field that indicates success or failure.
36///
37/// # Variants
38///
39/// * `Ok` - Successful response with a result value
40/// * `Error` - Error response with an error message
41///
42/// # Example JSON (Success)
43///
44/// ```json
45/// {
46///   "status": "ok",
47///   "id": "req-123",
48///   "result": 15
49/// }
50/// ```
51///
52/// # Example JSON (Error)
53///
54/// ```json
55/// {
56///   "status": "error",
57///   "id": "req-123",
58///   "error": "Invalid parameters"
59/// }
60/// ```
61#[derive(Debug, Serialize, Clone, PartialEq)]
62#[serde(tag = "status")]
63pub enum Response {
64    /// Successful response containing the result
65    #[serde(rename = "ok")]
66    Ok {
67        /// Request ID this response corresponds to
68        id: String,
69        /// The result value
70        result: Value,
71    },
72
73    /// Error response containing an error message
74    #[serde(rename = "error")]
75    Error {
76        /// Request ID this response corresponds to
77        id: String,
78        /// Human-readable error message
79        error: String,
80    },
81}
82
83/// An unsolicited event emitted by the binary process.
84#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
85pub struct Event {
86    /// Discriminator used by OpenCore to route the message as an event.
87    #[serde(rename = "type")]
88    pub kind: String,
89    /// Event name matched against `@BinaryEvent` handlers.
90    pub event: String,
91    /// Optional JSON payload delivered to the handler.
92    #[serde(skip_serializing_if = "Option::is_none")]
93    pub data: Option<Value>,
94}
95
96impl Event {
97    /// Creates a new event message.
98    pub fn new(event: impl Into<String>, data: Option<Value>) -> Self {
99        Self {
100            kind: "event".to_string(),
101            event: event.into(),
102            data,
103        }
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110    use serde_json::json;
111
112    #[test]
113    fn test_request_deserialization() {
114        let json_str = r#"{"id":"req-123","action":"sum","params":[5,10]}"#;
115        let req: Request = serde_json::from_str(json_str).unwrap();
116
117        assert_eq!(req.id, "req-123");
118        assert_eq!(req.action, "sum");
119        assert_eq!(req.params.len(), 2);
120        assert_eq!(req.params[0], json!(5));
121        assert_eq!(req.params[1], json!(10));
122    }
123
124    #[test]
125    fn test_request_with_empty_params() {
126        let json_str = r#"{"id":"req-456","action":"ping","params":[]}"#;
127        let req: Request = serde_json::from_str(json_str).unwrap();
128
129        assert_eq!(req.id, "req-456");
130        assert_eq!(req.action, "ping");
131        assert_eq!(req.params.len(), 0);
132    }
133
134    #[test]
135    fn test_response_ok_serialization() {
136        let response = Response::Ok {
137            id: "req-123".to_string(),
138            result: json!(15),
139        };
140
141        let json_str = serde_json::to_string(&response).unwrap();
142        let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
143
144        assert_eq!(parsed["status"], "ok");
145        assert_eq!(parsed["id"], "req-123");
146        assert_eq!(parsed["result"], 15);
147    }
148
149    #[test]
150    fn test_response_error_serialization() {
151        let response = Response::Error {
152            id: "req-456".to_string(),
153            error: "Invalid parameters".to_string(),
154        };
155
156        let json_str = serde_json::to_string(&response).unwrap();
157        let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
158
159        assert_eq!(parsed["status"], "error");
160        assert_eq!(parsed["id"], "req-456");
161        assert_eq!(parsed["error"], "Invalid parameters");
162    }
163
164    #[test]
165    fn test_request_clone() {
166        let req = Request {
167            id: "req-789".to_string(),
168            action: "test".to_string(),
169            params: vec![json!(1), json!(2)],
170        };
171
172        let cloned = req.clone();
173        assert_eq!(req, cloned);
174    }
175
176    #[test]
177    fn test_response_clone() {
178        let response = Response::Ok {
179            id: "req-999".to_string(),
180            result: json!("success"),
181        };
182
183        let cloned = response.clone();
184        assert_eq!(response, cloned);
185    }
186
187    #[test]
188    fn test_event_serialization() {
189        let event = Event::new("worker.ready", Some(json!({"pid": 42})));
190
191        let json_str = serde_json::to_string(&event).unwrap();
192        let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
193
194        assert_eq!(parsed["type"], "event");
195        assert_eq!(parsed["event"], "worker.ready");
196        assert_eq!(parsed["data"]["pid"], 42);
197    }
198
199    #[test]
200    fn test_event_without_data_serialization() {
201        let event = Event::new("heartbeat", None);
202
203        let json_str = serde_json::to_string(&event).unwrap();
204        let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
205
206        assert_eq!(parsed["type"], "event");
207        assert_eq!(parsed["event"], "heartbeat");
208        assert!(parsed.get("data").is_none());
209    }
210}