a2a_protocol_client/
error.rs1use std::fmt;
10
11use a2a_protocol_types::{A2aError, TaskId};
12
13#[derive(Debug)]
17#[non_exhaustive]
18pub enum ClientError {
19 Http(hyper::Error),
21
22 HttpClient(String),
24
25 Serialization(serde_json::Error),
27
28 Protocol(A2aError),
30
31 Transport(String),
33
34 InvalidEndpoint(String),
36
37 UnexpectedStatus {
39 status: u16,
41 body: String,
43 },
44
45 AuthRequired {
47 task_id: TaskId,
49 },
50
51 Timeout(String),
53}
54
55impl fmt::Display for ClientError {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 match self {
58 Self::Http(e) => write!(f, "HTTP error: {e}"),
59 Self::HttpClient(msg) => write!(f, "HTTP client error: {msg}"),
60 Self::Serialization(e) => write!(f, "serialization error: {e}"),
61 Self::Protocol(e) => write!(f, "protocol error: {e}"),
62 Self::Transport(msg) => write!(f, "transport error: {msg}"),
63 Self::InvalidEndpoint(msg) => write!(f, "invalid endpoint: {msg}"),
64 Self::UnexpectedStatus { status, body } => {
65 write!(f, "unexpected HTTP status {status}: {body}")
66 }
67 Self::AuthRequired { task_id } => {
68 write!(f, "authentication required for task: {task_id}")
69 }
70 Self::Timeout(msg) => write!(f, "timeout: {msg}"),
71 }
72 }
73}
74
75impl std::error::Error for ClientError {
76 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
77 match self {
78 Self::Http(e) => Some(e),
79 Self::Serialization(e) => Some(e),
80 Self::Protocol(e) => Some(e),
81 _ => None,
82 }
83 }
84}
85
86impl From<A2aError> for ClientError {
87 fn from(e: A2aError) -> Self {
88 Self::Protocol(e)
89 }
90}
91
92impl From<hyper::Error> for ClientError {
93 fn from(e: hyper::Error) -> Self {
94 Self::Http(e)
95 }
96}
97
98impl From<serde_json::Error> for ClientError {
99 fn from(e: serde_json::Error) -> Self {
100 Self::Serialization(e)
101 }
102}
103
104pub type ClientResult<T> = Result<T, ClientError>;
108
109#[cfg(test)]
112mod tests {
113 use super::*;
114 use a2a_protocol_types::ErrorCode;
115
116 #[test]
117 fn client_error_display_http_client() {
118 let e = ClientError::HttpClient("connection refused".into());
119 assert!(e.to_string().contains("connection refused"));
120 }
121
122 #[test]
123 fn client_error_display_protocol() {
124 let a2a = A2aError::task_not_found("task-99");
125 let e = ClientError::Protocol(a2a);
126 assert!(e.to_string().contains("task-99"));
127 }
128
129 #[test]
130 fn client_error_from_a2a_error() {
131 let a2a = A2aError::new(ErrorCode::TaskNotFound, "missing");
132 let e: ClientError = a2a.into();
133 assert!(matches!(e, ClientError::Protocol(_)));
134 }
135
136 #[test]
137 fn client_error_unexpected_status() {
138 let e = ClientError::UnexpectedStatus {
139 status: 404,
140 body: "Not Found".into(),
141 };
142 assert!(e.to_string().contains("404"));
143 }
144}