1use crate::jsonrpc::JsonRpcError;
7
8#[derive(Debug, thiserror::Error)]
16pub enum A2aError {
17 #[error("HTTP request failed: {0}")]
19 Http(#[from] reqwest::Error),
20
21 #[error("JSON serialization/deserialization failed: {0}")]
23 Json(#[from] serde_json::Error),
24
25 #[error("JSON-RPC error {code}: {message}")]
31 JsonRpc { code: i32, message: String },
32
33 #[error("agent discovery failed for {url}: {reason}")]
36 Discovery { url: String, reason: String },
37
38 #[error("SSE stream error: {0}")]
40 Stream(String),
41
42 #[error("server error: {0}")]
44 Server(String),
45
46 #[error("security policy violation: {0}")]
52 Security(String),
53}
54
55impl From<JsonRpcError> for A2aError {
56 fn from(e: JsonRpcError) -> Self {
57 Self::JsonRpc {
58 code: e.code,
59 message: e.message,
60 }
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn from_jsonrpc_error() {
70 let rpc_err = JsonRpcError {
71 code: -32001,
72 message: "task not found".into(),
73 data: None,
74 };
75 let err: A2aError = rpc_err.into();
76 match err {
77 A2aError::JsonRpc { code, message } => {
78 assert_eq!(code, -32001);
79 assert_eq!(message, "task not found");
80 }
81 _ => panic!("expected JsonRpc variant"),
82 }
83 }
84
85 #[test]
86 fn error_display() {
87 let err = A2aError::Discovery {
88 url: "http://example.com".into(),
89 reason: "connection refused".into(),
90 };
91 assert_eq!(
92 err.to_string(),
93 "agent discovery failed for http://example.com: connection refused"
94 );
95
96 let err = A2aError::Stream("unexpected EOF".into());
97 assert_eq!(err.to_string(), "SSE stream error: unexpected EOF");
98 }
99
100 #[test]
101 fn security_error_display() {
102 let err = A2aError::Security("TLS required but endpoint uses HTTP".into());
103 assert_eq!(
104 err.to_string(),
105 "security policy violation: TLS required but endpoint uses HTTP"
106 );
107 }
108
109 #[test]
110 fn from_serde_json_error() {
111 let json_err = serde_json::from_str::<serde_json::Value>("invalid").unwrap_err();
112 let err: A2aError = json_err.into();
113 assert!(matches!(err, A2aError::Json(_)));
114 }
115}