agent_client_protocol_schema/v1/
error.rs1use std::{fmt::Display, str};
14
15#[cfg(feature = "schemars")]
16use schemars::Schema;
17use serde::{Deserialize, Serialize};
18use serde_with::{DefaultOnError, serde_as, skip_serializing_none};
19
20use crate::IntoOption;
21
22pub type Result<T, E = Error> = std::result::Result<T, E>;
24
25#[serde_as]
32#[skip_serializing_none]
33#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
34#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
35#[non_exhaustive]
36pub struct Error {
37 pub code: ErrorCode,
40 pub message: String,
43 #[serde_as(deserialize_as = "DefaultOnError")]
46 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
47 #[serde(default)]
48 pub data: Option<serde_json::Value>,
49}
50
51impl Error {
52 #[must_use]
56 pub fn new(code: i32, message: impl Into<String>) -> Self {
57 Error {
58 code: code.into(),
59 message: message.into(),
60 data: None,
61 }
62 }
63
64 #[must_use]
69 pub fn data(mut self, data: impl IntoOption<serde_json::Value>) -> Self {
70 self.data = data.into_option();
71 self
72 }
73
74 #[must_use]
76 pub fn parse_error() -> Self {
77 ErrorCode::ParseError.into()
78 }
79
80 #[must_use]
82 pub fn invalid_request() -> Self {
83 ErrorCode::InvalidRequest.into()
84 }
85
86 #[must_use]
88 pub fn method_not_found() -> Self {
89 ErrorCode::MethodNotFound.into()
90 }
91
92 #[must_use]
94 pub fn invalid_params() -> Self {
95 ErrorCode::InvalidParams.into()
96 }
97
98 #[must_use]
100 pub fn internal_error() -> Self {
101 ErrorCode::InternalError.into()
102 }
103
104 #[must_use]
109 pub fn request_cancelled() -> Self {
110 ErrorCode::RequestCancelled.into()
111 }
112
113 #[must_use]
115 pub fn auth_required() -> Self {
116 ErrorCode::AuthRequired.into()
117 }
118
119 #[must_use]
121 pub fn resource_not_found(uri: Option<String>) -> Self {
122 let err: Self = ErrorCode::ResourceNotFound.into();
123 if let Some(uri) = uri {
124 err.data(serde_json::json!({ "uri": uri }))
125 } else {
126 err
127 }
128 }
129
130 #[must_use]
132 pub fn session_not_found(session_id: impl Into<super::SessionId>) -> Self {
133 Error::from(ErrorCode::ResourceNotFound)
134 .data(serde_json::json!({ "sessionId": session_id.into() }))
135 }
136
137 #[must_use]
141 pub fn into_internal_error(err: impl std::error::Error) -> Self {
142 Error::internal_error().data(err.to_string())
143 }
144}
145
146#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
152#[derive(Clone, Copy, Deserialize, Eq, PartialEq, Serialize, strum::Display)]
153#[cfg_attr(test, derive(strum::EnumIter))]
154#[serde(from = "i32", into = "i32")]
155#[cfg_attr(feature = "schemars", schemars(!from, !into))]
156#[non_exhaustive]
157pub enum ErrorCode {
158 #[cfg_attr(feature = "schemars", schemars(transform = error_code_transform))]
162 #[strum(to_string = "Parse error")]
163 ParseError, #[cfg_attr(feature = "schemars", schemars(transform = error_code_transform))]
166 #[strum(to_string = "Invalid request")]
167 InvalidRequest, #[cfg_attr(feature = "schemars", schemars(transform = error_code_transform))]
170 #[strum(to_string = "Method not found")]
171 MethodNotFound, #[cfg_attr(feature = "schemars", schemars(transform = error_code_transform))]
174 #[strum(to_string = "Invalid params")]
175 InvalidParams, #[cfg_attr(feature = "schemars", schemars(transform = error_code_transform))]
178 #[strum(to_string = "Internal error")]
179 InternalError, #[cfg_attr(feature = "schemars", schemars(transform = error_code_transform))]
184 #[strum(to_string = "Request cancelled")]
185 RequestCancelled, #[cfg_attr(feature = "schemars", schemars(transform = error_code_transform))]
190 #[strum(to_string = "Authentication required")]
191 AuthRequired, #[cfg_attr(feature = "schemars", schemars(transform = error_code_transform))]
194 #[strum(to_string = "Resource not found")]
195 ResourceNotFound, #[cfg_attr(feature = "schemars", schemars(untagged))]
198 #[strum(to_string = "Unknown error")]
199 Other(i32),
200}
201
202impl From<i32> for ErrorCode {
203 fn from(value: i32) -> Self {
204 match value {
205 -32700 => ErrorCode::ParseError,
206 -32600 => ErrorCode::InvalidRequest,
207 -32601 => ErrorCode::MethodNotFound,
208 -32602 => ErrorCode::InvalidParams,
209 -32603 => ErrorCode::InternalError,
210 -32800 => ErrorCode::RequestCancelled,
211 -32000 => ErrorCode::AuthRequired,
212 -32002 => ErrorCode::ResourceNotFound,
213 _ => ErrorCode::Other(value),
214 }
215 }
216}
217
218impl From<ErrorCode> for i32 {
219 fn from(value: ErrorCode) -> Self {
220 match value {
221 ErrorCode::ParseError => -32700,
222 ErrorCode::InvalidRequest => -32600,
223 ErrorCode::MethodNotFound => -32601,
224 ErrorCode::InvalidParams => -32602,
225 ErrorCode::InternalError => -32603,
226 ErrorCode::RequestCancelled => -32800,
227 ErrorCode::AuthRequired => -32000,
228 ErrorCode::ResourceNotFound => -32002,
229 ErrorCode::Other(value) => value,
230 }
231 }
232}
233
234impl std::fmt::Debug for ErrorCode {
235 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
236 write!(f, "{}: {self}", i32::from(*self))
237 }
238}
239
240#[cfg(feature = "schemars")]
241fn error_code_transform(schema: &mut Schema) {
242 let name = schema
243 .get("const")
244 .expect("Unexpected schema for ErrorCode")
245 .as_str()
246 .expect("unexpected type for schema");
247 let code = match name {
248 "ParseError" => ErrorCode::ParseError,
249 "InvalidRequest" => ErrorCode::InvalidRequest,
250 "MethodNotFound" => ErrorCode::MethodNotFound,
251 "InvalidParams" => ErrorCode::InvalidParams,
252 "InternalError" => ErrorCode::InternalError,
253 "RequestCancelled" => ErrorCode::RequestCancelled,
254 "AuthRequired" => ErrorCode::AuthRequired,
255 "ResourceNotFound" => ErrorCode::ResourceNotFound,
256 _ => panic!("Unexpected error code name {name}"),
257 };
258 let mut description = schema
259 .get("description")
260 .expect("Missing description")
261 .as_str()
262 .expect("Unexpected type for description")
263 .to_owned();
264 schema.insert("title".into(), code.to_string().into());
265 description.insert_str(0, &format!("**{code}**: "));
266 schema.insert("description".into(), description.into());
267 schema.insert("const".into(), i32::from(code).into());
268 schema.insert("type".into(), "integer".into());
269 schema.insert("format".into(), "int32".into());
270}
271
272impl From<ErrorCode> for Error {
273 fn from(error_code: ErrorCode) -> Self {
274 Error::new(error_code.into(), error_code.to_string())
275 }
276}
277
278impl std::error::Error for Error {}
279
280impl Display for Error {
281 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
282 if self.message.is_empty() {
283 write!(f, "{}", i32::from(self.code))?;
284 } else {
285 write!(f, "{}", self.message)?;
286 }
287
288 if let Some(data) = &self.data {
289 let pretty = serde_json::to_string_pretty(data).unwrap_or_else(|_| data.to_string());
290 write!(f, ": {pretty}")?;
291 }
292
293 Ok(())
294 }
295}
296
297impl From<anyhow::Error> for Error {
298 fn from(error: anyhow::Error) -> Self {
299 match error.downcast::<Self>() {
300 Ok(error) => error,
301 Err(error) => Error::into_internal_error(&*error),
302 }
303 }
304}
305
306impl From<serde_json::Error> for Error {
307 fn from(error: serde_json::Error) -> Self {
308 Error::invalid_params().data(error.to_string())
309 }
310}
311
312#[cfg(test)]
313mod tests {
314 use strum::IntoEnumIterator;
315
316 use super::*;
317
318 #[test]
319 fn serialize_error_code() {
320 assert_eq!(
321 serde_json::from_value::<ErrorCode>(serde_json::json!(-32700)).unwrap(),
322 ErrorCode::ParseError
323 );
324 assert_eq!(
325 serde_json::to_value(ErrorCode::ParseError).unwrap(),
326 serde_json::json!(-32700)
327 );
328
329 assert_eq!(
330 serde_json::from_value::<ErrorCode>(serde_json::json!(1)).unwrap(),
331 ErrorCode::Other(1)
332 );
333 assert_eq!(
334 serde_json::to_value(ErrorCode::Other(1)).unwrap(),
335 serde_json::json!(1)
336 );
337 }
338
339 #[test]
340 fn serialize_error_code_equality() {
341 #[cfg(feature = "schemars")]
343 let _schema = schemars::schema_for!(ErrorCode);
344 for error in ErrorCode::iter() {
345 assert_eq!(
346 error,
347 serde_json::from_value(serde_json::to_value(error).unwrap()).unwrap()
348 );
349 }
350 }
351}