opencore_jsonrpc_rust/
server.rs

1//! Binary JSON-RPC server implementation.
2//!
3//! This module provides a simple server that communicates via stdin/stdout
4//! using line-delimited JSON messages.
5
6use std::collections::HashMap;
7use std::io::{self, BufRead, Write};
8
9use serde_json::Value;
10
11use crate::protocol::{Request, Response};
12
13/// A handler function that processes JSON-RPC requests.
14///
15/// # Arguments
16///
17/// * `params` - Vector of JSON values representing the request parameters
18///
19/// # Returns
20///
21/// * `Ok(Value)` - Successful result as a JSON value
22/// * `Err(String)` - Error message describing what went wrong
23///
24/// # Example
25///
26/// ```rust
27/// use serde_json::Value;
28///
29/// fn add(params: Vec<Value>) -> Result<Value, String> {
30///     if params.len() != 2 {
31///         return Err("Expected 2 parameters".into());
32///     }
33///     let a = params[0].as_i64().ok_or("Invalid number")?;
34///     let b = params[1].as_i64().ok_or("Invalid number")?;
35///     Ok(Value::from(a + b))
36/// }
37/// ```
38pub type Handler = fn(Vec<Value>) -> Result<Value, String>;
39
40/// A JSON-RPC server that communicates via stdin/stdout.
41///
42/// The server reads line-delimited JSON requests from stdin and writes
43/// line-delimited JSON responses to stdout. This makes it easy to integrate
44/// with TypeScript or other language frameworks using process spawning.
45///
46/// # Example
47///
48/// ```no_run
49/// use opencore_jsonrpc_rust::server::BinaryServer;
50/// use serde_json::Value;
51///
52/// fn multiply(params: Vec<Value>) -> Result<Value, String> {
53///     if params.len() != 2 {
54///         return Err("Expected 2 parameters".into());
55///     }
56///     let a = params[0].as_f64().ok_or("Invalid number")?;
57///     let b = params[1].as_f64().ok_or("Invalid number")?;
58///     Ok(Value::from(a * b))
59/// }
60///
61/// fn main() {
62///     let mut server = BinaryServer::new();
63///     server.register("multiply", multiply);
64///     server.run();
65/// }
66/// ```
67#[derive(Debug)]
68pub struct BinaryServer {
69    handlers: HashMap<String, Handler>,
70}
71
72impl BinaryServer {
73    /// Creates a new empty server with no registered handlers.
74    ///
75    /// # Example
76    ///
77    /// ```rust
78    /// use opencore_jsonrpc_rust::server::BinaryServer;
79    ///
80    /// let server = BinaryServer::new();
81    /// ```
82    pub fn new() -> Self {
83        Self {
84            handlers: HashMap::new(),
85        }
86    }
87
88    /// Registers a handler function for a specific action name.
89    ///
90    /// When a request with the given action is received, the corresponding
91    /// handler will be invoked with the request parameters.
92    ///
93    /// # Arguments
94    ///
95    /// * `action` - The action name to register (e.g., "sum", "multiply")
96    /// * `handler` - The handler function to invoke for this action
97    ///
98    /// # Example
99    ///
100    /// ```rust
101    /// use opencore_jsonrpc_rust::server::BinaryServer;
102    /// use serde_json::Value;
103    ///
104    /// fn echo(params: Vec<Value>) -> Result<Value, String> {
105    ///     params.first()
106    ///         .cloned()
107    ///         .ok_or_else(|| "No parameter provided".into())
108    /// }
109    ///
110    /// let mut server = BinaryServer::new();
111    /// server.register("echo", echo);
112    /// ```
113    pub fn register(&mut self, action: &str, handler: Handler) {
114        self.handlers.insert(action.to_string(), handler);
115    }
116
117    /// Starts the server loop, reading from stdin and writing to stdout.
118    ///
119    /// This method blocks indefinitely, processing requests as they arrive.
120    /// Each line from stdin should be a valid JSON request. Responses are
121    /// written as JSON lines to stdout.
122    ///
123    /// # Panics
124    ///
125    /// This method will not panic under normal circumstances. I/O errors
126    /// are handled gracefully by continuing to the next request.
127    pub fn run(&self) {
128        let stdin = io::stdin();
129        let mut stdout = io::stdout();
130
131        for line in stdin.lock().lines() {
132            let line = match line {
133                Ok(l) => l,
134                Err(_) => continue,
135            };
136
137            let response = match serde_json::from_str::<Request>(&line) {
138                Ok(req) => self.handle_request(req),
139                Err(err) => {
140                    let raw = format!(r#"{{"status":"error","error":"invalid json: {}"}}"#, err);
141                    let _ = writeln!(stdout, "{}", raw);
142                    let _ = stdout.flush();
143                    continue;
144                }
145            };
146
147            match serde_json::to_string(&response) {
148                Ok(out) => {
149                    let _ = writeln!(stdout, "{}", out);
150                    let _ = stdout.flush();
151                }
152                Err(err) => {
153                    let raw = format!(
154                        r#"{{"status":"error","error":"serialization failed: {}"}}"#,
155                        err
156                    );
157                    let _ = writeln!(stdout, "{}", raw);
158                    let _ = stdout.flush();
159                }
160            }
161        }
162    }
163
164    /// Handles a single request by dispatching to the appropriate handler.
165    ///
166    /// # Arguments
167    ///
168    /// * `req` - The parsed request to handle
169    ///
170    /// # Returns
171    ///
172    /// A response indicating success or failure
173    fn handle_request(&self, req: Request) -> Response {
174        match self.handlers.get(&req.action) {
175            Some(handler) => match handler(req.params) {
176                Ok(result) => Response::Ok { id: req.id, result },
177                Err(msg) => Response::Error {
178                    id: req.id,
179                    error: msg,
180                },
181            },
182            None => Response::Error {
183                id: req.id,
184                error: format!("unknown action: {}", req.action),
185            },
186        }
187    }
188}
189
190impl Default for BinaryServer {
191    fn default() -> Self {
192        Self::new()
193    }
194}
195
196#[cfg(test)]
197mod tests {
198    use super::*;
199    use serde_json::json;
200
201    fn test_handler_success(params: Vec<Value>) -> Result<Value, String> {
202        Ok(json!({"received": params.len()}))
203    }
204
205    fn test_handler_error(_params: Vec<Value>) -> Result<Value, String> {
206        Err("intentional error".to_string())
207    }
208
209    fn add_handler(params: Vec<Value>) -> Result<Value, String> {
210        if params.len() != 2 {
211            return Err("Expected 2 parameters".into());
212        }
213        let a = params[0].as_i64().ok_or("Invalid number")?;
214        let b = params[1].as_i64().ok_or("Invalid number")?;
215        Ok(Value::from(a + b))
216    }
217
218    #[test]
219    fn test_server_creation() {
220        let server = BinaryServer::new();
221        assert_eq!(server.handlers.len(), 0);
222    }
223
224    #[test]
225    fn test_server_default() {
226        let server = BinaryServer::default();
227        assert_eq!(server.handlers.len(), 0);
228    }
229
230    #[test]
231    fn test_register_handler() {
232        let mut server = BinaryServer::new();
233        server.register("test", test_handler_success);
234        assert_eq!(server.handlers.len(), 1);
235        assert!(server.handlers.contains_key("test"));
236    }
237
238    #[test]
239    fn test_register_multiple_handlers() {
240        let mut server = BinaryServer::new();
241        server.register("handler1", test_handler_success);
242        server.register("handler2", test_handler_error);
243        assert_eq!(server.handlers.len(), 2);
244    }
245
246    #[test]
247    fn test_handle_request_success() {
248        let mut server = BinaryServer::new();
249        server.register("test", test_handler_success);
250
251        let request = Request {
252            id: "req-1".to_string(),
253            action: "test".to_string(),
254            params: vec![json!(1), json!(2)],
255        };
256
257        let response = server.handle_request(request);
258
259        match response {
260            Response::Ok { id, result } => {
261                assert_eq!(id, "req-1");
262                assert_eq!(result, json!({"received": 2}));
263            }
264            _ => panic!("Expected Ok response"),
265        }
266    }
267
268    #[test]
269    fn test_handle_request_handler_error() {
270        let mut server = BinaryServer::new();
271        server.register("test", test_handler_error);
272
273        let request = Request {
274            id: "req-2".to_string(),
275            action: "test".to_string(),
276            params: vec![],
277        };
278
279        let response = server.handle_request(request);
280
281        match response {
282            Response::Error { id, error } => {
283                assert_eq!(id, "req-2");
284                assert_eq!(error, "intentional error");
285            }
286            _ => panic!("Expected Error response"),
287        }
288    }
289
290    #[test]
291    fn test_handle_request_unknown_action() {
292        let server = BinaryServer::new();
293
294        let request = Request {
295            id: "req-3".to_string(),
296            action: "nonexistent".to_string(),
297            params: vec![],
298        };
299
300        let response = server.handle_request(request);
301
302        match response {
303            Response::Error { id, error } => {
304                assert_eq!(id, "req-3");
305                assert_eq!(error, "unknown action: nonexistent");
306            }
307            _ => panic!("Expected Error response"),
308        }
309    }
310
311    #[test]
312    fn test_add_handler_success() {
313        let mut server = BinaryServer::new();
314        server.register("add", add_handler);
315
316        let request = Request {
317            id: "req-4".to_string(),
318            action: "add".to_string(),
319            params: vec![json!(5), json!(10)],
320        };
321
322        let response = server.handle_request(request);
323
324        match response {
325            Response::Ok { id, result } => {
326                assert_eq!(id, "req-4");
327                assert_eq!(result, json!(15));
328            }
329            _ => panic!("Expected Ok response"),
330        }
331    }
332
333    #[test]
334    fn test_add_handler_wrong_param_count() {
335        let mut server = BinaryServer::new();
336        server.register("add", add_handler);
337
338        let request = Request {
339            id: "req-5".to_string(),
340            action: "add".to_string(),
341            params: vec![json!(5)],
342        };
343
344        let response = server.handle_request(request);
345
346        match response {
347            Response::Error { id, error } => {
348                assert_eq!(id, "req-5");
349                assert_eq!(error, "Expected 2 parameters");
350            }
351            _ => panic!("Expected Error response"),
352        }
353    }
354
355    #[test]
356    fn test_add_handler_invalid_type() {
357        let mut server = BinaryServer::new();
358        server.register("add", add_handler);
359
360        let request = Request {
361            id: "req-6".to_string(),
362            action: "add".to_string(),
363            params: vec![json!("not a number"), json!(10)],
364        };
365
366        let response = server.handle_request(request);
367
368        match response {
369            Response::Error { id, error } => {
370                assert_eq!(id, "req-6");
371                assert_eq!(error, "Invalid number");
372            }
373            _ => panic!("Expected Error response"),
374        }
375    }
376}