agent_client_protocol/
util.rs1use futures::{
4 future::BoxFuture,
5 stream::{Stream, StreamExt},
6};
7
8mod typed;
9pub use typed::{MatchDispatch, MatchDispatchFrom, TypeNotification};
10
11pub(crate) fn redacted_error(code: impl Into<i32>) -> impl std::fmt::Debug {
17 struct RedactedError {
18 code: i32,
19 reason: &'static str,
20 }
21
22 impl std::fmt::Debug for RedactedError {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 f.debug_struct("Error")
25 .field("code", &self.code)
26 .field("reason", &self.reason)
27 .finish()
28 }
29 }
30
31 let code = code.into();
32 let reason = match code {
33 -32700 => "Parse error",
34 -32600 => "Invalid request",
35 -32601 => "Method not found",
36 -32602 => "Invalid params",
37 -32603 => "Internal error",
38 _ => "Protocol or application error",
39 };
40 RedactedError { code, reason }
41}
42
43fn redact_env_and_header_values(value: &mut serde_json::Value) {
51 match value {
52 serde_json::Value::Object(object) => {
53 for (key, field) in object.iter_mut() {
54 if key == "env" || key == "headers" {
55 redact_name_value_pairs(field);
56 }
57 redact_env_and_header_values(field);
58 }
59 }
60 serde_json::Value::Array(values) => {
61 values.iter_mut().for_each(redact_env_and_header_values);
62 }
63 _ => {}
64 }
65}
66
67fn redact_name_value_pairs(field: &mut serde_json::Value) {
69 let redacted = || serde_json::Value::String("[REDACTED]".to_owned());
70 match field {
71 serde_json::Value::Array(entries) => {
72 for entry in entries {
73 if let Some(value) = entry.get_mut("value") {
74 *value = redacted();
75 }
76 }
77 }
78 serde_json::Value::Object(entries) => {
79 for value in entries.values_mut() {
80 *value = redacted();
81 }
82 }
83 _ => {}
84 }
85}
86
87pub(crate) fn redacted_json_line(line: &str) -> impl std::fmt::Display + '_ {
92 struct RedactedJsonLine<'a>(&'a str);
93
94 impl std::fmt::Display for RedactedJsonLine<'_> {
95 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 match serde_json::from_str::<serde_json::Value>(self.0) {
97 Ok(mut value) => {
98 redact_env_and_header_values(&mut value);
99 write!(f, "{value}")
100 }
101 Err(_) => write!(f, "<{} bytes of invalid JSON omitted>", self.0.len()),
102 }
103 }
104 }
105
106 RedactedJsonLine(line)
107}
108
109pub fn json_cast<N, M>(params: N) -> Result<M, crate::Error>
114where
115 N: serde::Serialize,
116 M: serde::de::DeserializeOwned,
117{
118 let json = serde_json::to_value(params).map_err(|e| {
119 crate::Error::parse_error().data(serde_json::json!({
120 "error": e.to_string(),
121 "phase": "serialization"
122 }))
123 })?;
124 let m = serde_json::from_value(json.clone()).map_err(|e| {
125 crate::Error::parse_error().data(serde_json::json!({
126 "error": e.to_string(),
127 "json": json,
128 "phase": "deserialization"
129 }))
130 })?;
131 Ok(m)
132}
133
134pub fn json_cast_params<N, M>(params: N) -> Result<M, crate::Error>
144where
145 N: serde::Serialize,
146 M: serde::de::DeserializeOwned,
147{
148 let json = serde_json::to_value(params).map_err(|e| {
149 crate::Error::internal_error().data(serde_json::json!({
150 "error": e.to_string(),
151 "phase": "serialization"
152 }))
153 })?;
154 let m = serde_json::from_value(json.clone()).map_err(|e| {
155 crate::Error::invalid_params().data(serde_json::json!({
156 "error": e.to_string(),
157 "json": json,
158 "phase": "deserialization"
159 }))
160 })?;
161 Ok(m)
162}
163
164pub fn internal_error(message: impl ToString) -> crate::Error {
166 crate::Error::internal_error().data(message.to_string())
167}
168
169pub fn parse_error(message: impl ToString) -> crate::Error {
171 crate::Error::parse_error().data(message.to_string())
172}
173
174#[must_use]
179pub fn unsupported_protocol_version(
180 requested: crate::schema::ProtocolVersion,
181 supported: &[crate::schema::ProtocolVersion],
182) -> crate::Error {
183 crate::Error::new(-32022, "Unsupported protocol version")
184 .data(serde_json::json!({ "requested": requested, "supported": supported }))
185}
186
187pub(crate) fn instrumented_with_connection_name<F>(
188 name: String,
189 task: F,
190) -> tracing::instrument::Instrumented<F> {
191 use tracing::Instrument;
192
193 task.instrument(tracing::info_span!("connection", name = name))
194}
195
196pub(crate) async fn instrument_with_connection_name<R>(
197 name: Option<String>,
198 task: impl Future<Output = R>,
199) -> R {
200 if let Some(name) = name {
201 instrumented_with_connection_name(name.clone(), task).await
202 } else {
203 task.await
204 }
205}
206
207pub fn run_until<T, E>(
213 background: impl Future<Output = Result<(), E>>,
214 foreground: impl Future<Output = Result<T, E>>,
215) -> impl Future<Output = Result<T, E>> {
216 use futures::future::{Either, select};
217 use std::pin::pin;
218
219 Box::pin(async move {
220 match select(pin!(background), pin!(foreground)).await {
221 Either::Left((bg_result, fg_future)) => {
222 bg_result?; fg_future.await
225 }
226 Either::Right((fg_result, _bg_future)) => {
227 fg_result
229 }
230 }
231 })
232}
233
234pub(crate) async fn process_stream_concurrently<T, F>(
243 stream: impl Stream<Item = T>,
244 process_fn: F,
245 process_fn_hack: impl for<'a> Fn(&'a F, T) -> BoxFuture<'a, Result<(), crate::Error>>,
246) -> Result<(), crate::Error>
247where
248 F: AsyncFn(T) -> Result<(), crate::Error>,
249{
250 use std::pin::pin;
251
252 use futures::stream::{FusedStream, FuturesUnordered};
253 use futures_concurrency::future::Race;
254
255 enum Event<T> {
256 NewItem(Option<T>),
257 FutureCompleted(Option<Result<(), crate::Error>>),
258 }
259
260 let mut stream = pin!(stream.fuse());
261 let mut futures: FuturesUnordered<_> = FuturesUnordered::new();
262
263 loop {
264 if futures.is_empty() {
266 match stream.next().await {
267 Some(item) => futures.push(process_fn_hack(&process_fn, item)),
268 None => return Ok(()),
269 }
270 continue;
271 }
272
273 if stream.is_terminated() {
275 while let Some(result) = futures.next().await {
276 result?;
277 }
278 return Ok(());
279 }
280
281 let event = (async { Event::NewItem(stream.next().await) }, async {
283 Event::FutureCompleted(futures.next().await)
284 })
285 .race()
286 .await;
287
288 match event {
289 Event::NewItem(Some(item)) => {
290 futures.push(process_fn_hack(&process_fn, item));
291 }
292 Event::FutureCompleted(Some(result)) => {
293 result?;
294 }
295 Event::NewItem(None) | Event::FutureCompleted(None) => {
296 }
299 }
300 }
301}