1use std::io::{BufRead, Write};
19use std::sync::Arc;
20
21use serde_json::{Value, json};
22
23pub trait NativeBackend: Send {
26 fn init(&mut self, init: &Value, out: &Outbox) {
28 let _ = (init, out);
29 }
30
31 fn event(&mut self, event: &Value, out: &Outbox) {
33 let _ = (event, out);
34 }
35
36 fn request(&mut self, method: &str, params: &Value, out: &Outbox) -> Result<Value, String>;
39
40 fn after_request(&mut self, method: &str, out: &Outbox) {
42 let _ = (method, out);
43 }
44
45 fn shutdown(&mut self) {}
47}
48
49pub type NativeFactory = Arc<dyn Fn() -> Box<dyn NativeBackend> + Send + Sync>;
51
52#[derive(Clone)]
54pub struct Outbox(Arc<dyn Fn(Value) + Send + Sync>);
55
56impl Outbox {
57 pub fn new(send: impl Fn(Value) + Send + Sync + 'static) -> Self {
58 Self(Arc::new(send))
59 }
60
61 pub fn send(&self, msg: Value) {
63 (self.0)(msg);
64 }
65
66 pub fn view(&self, id: &str, key: &str, tree: Value) {
68 self.send(json!({"type": "view", "id": id, "key": key, "view": tree}));
69 }
70
71 pub fn notify(&self, msg: Value, client: Option<u64>) {
73 self.send(json!({"type": "notify", "msg": msg, "client": client}));
74 }
75
76 pub fn notice(&self, text: &str, level: &str) {
78 self.send(json!({"type": "notice", "text": text, "level": level}));
79 }
80
81 pub fn log(&self, text: &str) {
83 self.send(json!({"type": "log", "text": text}));
84 }
85
86 pub fn fault(&self, text: &str) {
88 self.send(json!({"type": "fault", "text": text}));
89 }
90
91 pub fn subscribe(&self, events: &[&str]) {
93 self.send(json!({"type": "subscribe", "events": events}));
94 }
95}
96
97pub fn dispatch(backend: &mut dyn NativeBackend, msg: &Value, out: &Outbox) -> bool {
100 match msg.get("type").and_then(Value::as_str).unwrap_or_default() {
101 "init" => backend.init(msg, out),
102 "event" => backend.event(msg, out),
103 "request" => {
104 let id = msg.get("id").cloned().unwrap_or(Value::Null);
105 let method = msg.get("method").and_then(Value::as_str).unwrap_or_default();
106 let params = msg.get("params").unwrap_or(&Value::Null);
107 let reply = match backend.request(method, params, out) {
108 Ok(result) => json!({"type": "reply", "id": id, "result": result}),
109 Err(error) => json!({"type": "reply", "id": id, "error": error}),
110 };
111 out.send(reply);
112 backend.after_request(method, out);
113 }
114 "shutdown" => {
115 backend.shutdown();
116 return false;
117 }
118 _ => {}
120 }
121 true
122}
123
124pub fn serve_stdio(mut backend: Box<dyn NativeBackend>) -> std::io::Result<()> {
127 let stdout = std::sync::Mutex::new(std::io::stdout());
128 let out = Outbox::new(move |msg| {
129 if let Ok(mut line) = serde_json::to_vec(&msg) {
130 line.push(b'\n');
131 let mut w = stdout.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
132 let _ = w.write_all(&line).and_then(|()| w.flush());
133 }
134 });
135 let stdin = std::io::stdin();
136 for line in stdin.lock().lines() {
137 let line = line?;
138 let Ok(msg) = serde_json::from_str::<Value>(line.trim()) else { continue };
139 if !msg.is_object() {
140 continue;
141 }
142 if !dispatch(backend.as_mut(), &msg, &out) {
143 break;
144 }
145 }
146 Ok(())
147}
148
149pub fn panic_text(payload: &(dyn std::any::Any + Send)) -> String {
151 payload.downcast_ref::<&str>().map(|s| (*s).to_owned()).or_else(|| payload.downcast_ref::<String>().cloned()).unwrap_or_else(|| "(no message)".into())
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157 use std::sync::Mutex;
158
159 struct Echo;
160 impl NativeBackend for Echo {
161 fn request(&mut self, method: &str, params: &Value, out: &Outbox) -> Result<Value, String> {
162 out.log("working");
163 match method {
164 "tool" => Ok(params.get("args").cloned().unwrap_or(Value::Null)),
165 _ => Err(format!("no {method}")),
166 }
167 }
168 fn after_request(&mut self, _: &str, out: &Outbox) {
169 out.view("v", "", json!({"type": "text", "text": "after"}));
170 }
171 }
172
173 #[test]
174 fn dispatch_replies_in_order() {
175 let seen = Arc::new(Mutex::new(Vec::new()));
176 let s = Arc::clone(&seen);
177 let out = Outbox::new(move |m| s.lock().unwrap().push(m));
178 let mut b = Echo;
179 assert!(dispatch(&mut b, &json!({"type": "request", "id": 3, "method": "tool", "params": {"args": {"x": 1}}}), &out));
180 assert!(dispatch(&mut b, &json!({"type": "request", "id": 4, "method": "nope"}), &out));
181 assert!(!dispatch(&mut b, &json!({"type": "shutdown"}), &out));
182 let seen = seen.lock().unwrap();
183 let types: Vec<&str> = seen.iter().map(|m| m["type"].as_str().unwrap()).collect();
184 assert_eq!(types, ["log", "reply", "view", "log", "reply", "view"]);
185 assert_eq!(seen[1]["result"], json!({"x": 1}));
186 assert_eq!(seen[4]["error"], "no nope");
187 }
188}