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