use std::fmt;
use a2a_protocol_types::error::{A2aError, ErrorCode};
use a2a_protocol_types::task::TaskId;
#[derive(Debug)]
#[non_exhaustive]
pub enum ServerError {
TaskNotFound(TaskId),
TaskNotCancelable(TaskId),
InvalidParams(String),
Serialization(serde_json::Error),
Http(hyper::Error),
HttpClient(String),
Transport(String),
PushNotSupported,
Internal(String),
MethodNotFound(String),
Protocol(A2aError),
PayloadTooLarge(String),
UnsupportedOperation(String),
InvalidStateTransition {
task_id: TaskId,
from: a2a_protocol_types::task::TaskState,
to: a2a_protocol_types::task::TaskState,
},
Overloaded(String),
}
impl fmt::Display for ServerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::TaskNotFound(id) => write!(f, "task not found: {id}"),
Self::TaskNotCancelable(id) => write!(f, "task not cancelable: {id}"),
Self::InvalidParams(msg) => write!(f, "invalid params: {msg}"),
Self::Serialization(e) => write!(f, "serialization error: {e}"),
Self::Http(e) => write!(f, "HTTP error: {e}"),
Self::HttpClient(msg) => write!(f, "HTTP client error: {msg}"),
Self::Transport(msg) => write!(f, "transport error: {msg}"),
Self::PushNotSupported => f.write_str("push notifications not supported"),
Self::UnsupportedOperation(msg) => write!(f, "unsupported operation: {msg}"),
Self::Internal(msg) => write!(f, "internal error: {msg}"),
Self::MethodNotFound(m) => write!(f, "method not found: {m}"),
Self::Protocol(e) => write!(f, "protocol error: {e}"),
Self::PayloadTooLarge(msg) => write!(f, "payload too large: {msg}"),
Self::InvalidStateTransition { task_id, from, to } => {
write!(
f,
"invalid state transition for task {task_id}: {from} → {to}"
)
}
Self::Overloaded(msg) => write!(f, "server overloaded: {msg}"),
}
}
}
impl std::error::Error for ServerError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Serialization(e) => Some(e),
Self::Http(e) => Some(e),
Self::Protocol(e) => Some(e),
_ => None,
}
}
}
impl ServerError {
#[must_use]
pub const fn metric_label(&self) -> &'static str {
match self {
Self::TaskNotFound(_) => "task_not_found",
Self::TaskNotCancelable(_) => "task_not_cancelable",
Self::InvalidParams(_) => "invalid_params",
Self::Serialization(_) => "serialization",
Self::Http(_) => "http",
Self::HttpClient(_) => "http_client",
Self::Transport(_) => "transport",
Self::PushNotSupported => "push_not_supported",
Self::Internal(_) => "internal",
Self::MethodNotFound(_) => "method_not_found",
Self::Protocol(_) => "protocol",
Self::PayloadTooLarge(_) => "payload_too_large",
Self::UnsupportedOperation(_) => "unsupported_operation",
Self::InvalidStateTransition { .. } => "invalid_state_transition",
Self::Overloaded(_) => "overloaded",
}
}
#[must_use]
pub fn to_a2a_error(&self) -> A2aError {
match self {
Self::TaskNotFound(id) => A2aError::task_not_found(id),
Self::TaskNotCancelable(id) => A2aError::task_not_cancelable(id),
Self::InvalidParams(msg) => A2aError::invalid_params(msg.clone()),
Self::Serialization(e) => A2aError::parse_error(e.to_string()),
Self::MethodNotFound(m) => {
A2aError::new(ErrorCode::MethodNotFound, format!("Method not found: {m}"))
}
Self::PushNotSupported => A2aError::new(
ErrorCode::PushNotificationNotSupported,
"Push notifications not supported",
),
Self::UnsupportedOperation(msg) => {
A2aError::new(ErrorCode::UnsupportedOperation, msg.clone())
}
Self::Protocol(e) => e.clone(),
Self::Http(e) => A2aError::internal(e.to_string()),
Self::HttpClient(msg) | Self::Transport(msg) | Self::Internal(msg) => {
A2aError::internal(msg.clone())
}
Self::PayloadTooLarge(msg) => A2aError::new(ErrorCode::InvalidRequest, msg.clone()),
Self::InvalidStateTransition { task_id, from, to } => A2aError::invalid_params(
format!("invalid state transition for task {task_id}: {from} → {to}"),
),
Self::Overloaded(msg) => A2aError::internal(msg.clone()),
}
}
}
impl From<A2aError> for ServerError {
fn from(e: A2aError) -> Self {
Self::Protocol(e)
}
}
impl From<serde_json::Error> for ServerError {
fn from(e: serde_json::Error) -> Self {
Self::Serialization(e)
}
}
impl From<hyper::Error> for ServerError {
fn from(e: hyper::Error) -> Self {
Self::Http(e)
}
}
pub type ServerResult<T> = Result<T, ServerError>;
#[cfg(test)]
mod tests;