lsp_max/client/
builder.rs1use crate::client::server_handle::ServerHandle;
2use crate::client::LanguageClient;
3use serde_json::Value;
4use std::collections::HashMap;
5use std::sync::atomic::AtomicU64;
6use std::sync::Arc;
7use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, BufReader};
8use tokio::sync::{mpsc, oneshot, Mutex};
9
10pub struct ClientBuilder {
12 }
14
15impl ClientBuilder {
16 pub fn new() -> Self {
18 Self {}
19 }
20
21 pub fn build<C, I, O>(self, _client: C, input: I, output: O) -> ServerHandle
27 where
28 C: LanguageClient + Send + 'static,
29 I: AsyncRead + Unpin + Send + 'static,
30 O: AsyncWrite + Unpin + Send + 'static,
31 {
32 let pending: Arc<Mutex<HashMap<u64, oneshot::Sender<Value>>>> =
33 Arc::new(Mutex::new(HashMap::new()));
34 let next_id = Arc::new(AtomicU64::new(1));
35 let (tx, rx) = mpsc::channel::<Value>(64);
36
37 let handle = ServerHandle::new(tx, Arc::clone(&pending), Arc::clone(&next_id));
38
39 tokio::spawn(write_loop(rx, output));
41
42 tokio::spawn(read_loop(input, Arc::clone(&pending), _client));
44
45 handle
46 }
47}
48
49impl Default for ClientBuilder {
50 fn default() -> Self {
51 Self::new()
52 }
53}
54
55async fn write_loop<O: AsyncWrite + Unpin>(mut rx: mpsc::Receiver<Value>, mut output: O) {
57 while let Some(msg) = rx.recv().await {
58 match serde_json::to_vec(&msg) {
59 Ok(body) => {
60 let header = format!("Content-Length: {}\r\n\r\n", body.len());
61 if output.write_all(header.as_bytes()).await.is_err() {
62 break;
63 }
64 if output.write_all(&body).await.is_err() {
65 break;
66 }
67 }
68 Err(e) => {
69 tracing::warn!(
70 "lsp-max-client: failed to serialize outbound message: {}",
71 e
72 );
73 }
74 }
75 }
76}
77
78async fn read_loop<I: AsyncRead + Unpin, C: LanguageClient>(
81 input: I,
82 pending: Arc<Mutex<HashMap<u64, oneshot::Sender<Value>>>>,
83 _client: C,
84) {
85 let mut reader = BufReader::new(input);
86
87 loop {
88 let mut content_length: Option<usize> = None;
90 loop {
91 let mut line = String::new();
92 match reader.read_line(&mut line).await {
93 Ok(0) => return, Err(e) => {
95 tracing::debug!("lsp-max-client: read_line error: {}", e);
96 return;
97 }
98 Ok(_) => {}
99 }
100 let trimmed = line.trim_end_matches(['\r', '\n']);
101 if trimmed.is_empty() {
102 break;
104 }
105 if let Some(rest) = trimmed.strip_prefix("Content-Length:") {
106 match rest.trim().parse::<usize>() {
107 Ok(n) => content_length = Some(n),
108 Err(e) => {
109 tracing::warn!("lsp-max-client: bad Content-Length: {}", e);
110 }
111 }
112 }
113 }
114
115 let length = match content_length {
116 Some(n) => n,
117 None => {
118 tracing::warn!("lsp-max-client: no Content-Length header found, skipping");
119 continue;
120 }
121 };
122
123 let mut body = vec![0u8; length];
125 if let Err(e) = reader.read_exact(&mut body).await {
126 tracing::debug!("lsp-max-client: read_exact error: {}", e);
127 return;
128 }
129
130 let msg: Value = match serde_json::from_slice(&body) {
131 Ok(v) => v,
132 Err(e) => {
133 tracing::warn!("lsp-max-client: failed to parse JSON body: {}", e);
134 continue;
135 }
136 };
137
138 let has_id = msg.get("id").map(|v| !v.is_null()).unwrap_or(false);
140 let has_method = msg.get("method").is_some();
141
142 if has_id && !has_method {
143 if let Some(id) = msg["id"].as_u64() {
145 let result = msg.get("result").cloned().unwrap_or(Value::Null);
146 if let Some(tx) = pending.lock().await.remove(&id) {
147 let _ = tx.send(result);
148 }
149 }
150 } else if has_method {
151 let method = msg["method"].as_str().unwrap_or("").to_owned();
153 tracing::debug!("lsp-max-client: notification from server: {}", method);
154 }
156 }
157}