turul_a2a_client/
error.rs1#[derive(Debug, thiserror::Error)]
4#[non_exhaustive]
5pub enum A2aClientError {
6 #[error("HTTP {status}: {message}")]
8 Http { status: u16, message: String },
9
10 #[error("A2A error {status}: {message}")]
12 A2aError {
13 status: u16,
14 message: String,
15 reason: Option<String>,
16 },
17
18 #[error("Request error: {0}")]
20 Request(#[from] reqwest::Error),
21
22 #[error("JSON error: {0}")]
24 Json(#[from] serde_json::Error),
25
26 #[error("Type conversion error: {0}")]
28 Conversion(String),
29
30 #[error("SSE error: {0}")]
32 Sse(String),
33
34 #[error("SSE stream closed")]
36 StreamClosed,
37
38 #[cfg(feature = "grpc")]
43 #[error("gRPC error {0:?}: {1}")]
44 Grpc(tonic::Code, String, Option<String>),
45
46 #[cfg(feature = "grpc")]
49 #[error("gRPC transport error: {0}")]
50 GrpcTransport(String),
51}
52
53impl A2aClientError {
54 pub fn reason(&self) -> Option<&str> {
56 match self {
57 Self::A2aError { reason, .. } => reason.as_deref(),
58 #[cfg(feature = "grpc")]
59 Self::Grpc(_, _, reason) => reason.as_deref(),
60 _ => None,
61 }
62 }
63
64 pub fn status(&self) -> Option<u16> {
66 match self {
67 Self::Http { status, .. } | Self::A2aError { status, .. } => Some(*status),
68 _ => None,
69 }
70 }
71
72 #[cfg(feature = "grpc")]
74 pub fn grpc_code(&self) -> Option<tonic::Code> {
75 match self {
76 Self::Grpc(code, _, _) => Some(*code),
77 _ => None,
78 }
79 }
80}
81
82#[cfg(feature = "grpc")]
83impl From<tonic::Status> for A2aClientError {
84 fn from(status: tonic::Status) -> Self {
85 let reason = {
88 use tonic_types::StatusExt;
89 status.get_details_error_info().map(|info| info.reason)
90 };
91 A2aClientError::Grpc(status.code(), status.message().to_string(), reason)
92 }
93}
94
95#[cfg(feature = "grpc")]
96impl From<tonic::transport::Error> for A2aClientError {
97 fn from(err: tonic::transport::Error) -> Self {
98 A2aClientError::GrpcTransport(err.to_string())
99 }
100}