use std::time::Duration;
use crate::wire::cbor::CborError;
use crate::wire::frame::FrameError;
use crate::wire::types::{ErrorCategoryWire, ErrorCodeWire, ErrorResponse};
pub type Result<T> = std::result::Result<T, BrainError>;
#[derive(Debug, thiserror::Error)]
pub enum BrainError {
#[error("io error: {0}")]
Io(#[from] std::io::Error),
#[error("frame codec error: {0}")]
Frame(#[from] FrameError),
#[error("payload codec error: {0}")]
Cbor(#[from] CborError),
#[error("protocol violation: {0}")]
Protocol(String),
#[error("server error [{code:?} / {category:?}]: {message}")]
Server {
code: ErrorCodeWire,
category: ErrorCategoryWire,
message: String,
retry_after_ms: Option<u32>,
response: Box<ErrorResponse>,
},
#[error("version mismatch: server chose {chosen}, client supports {supported:?}")]
VersionMismatch {
chosen: u8,
supported: Vec<u8>,
},
#[error("connection closed by peer")]
Closed,
#[error("operation timed out after {0:?}")]
Timeout(Duration),
}
impl BrainError {
#[must_use]
pub fn from_server(response: ErrorResponse) -> Self {
BrainError::Server {
code: response.code,
category: response.category,
message: response.message.clone(),
retry_after_ms: response.retry_after_ms,
response: Box::new(response),
}
}
#[must_use]
pub fn is_retryable(&self) -> bool {
match self {
BrainError::Io(_) | BrainError::Closed | BrainError::Timeout(_) => true,
BrainError::Server { category, .. } => matches!(
category,
ErrorCategoryWire::ResourceExhausted | ErrorCategoryWire::Unavailable
),
_ => false,
}
}
#[must_use]
pub fn retry_after(&self) -> Option<Duration> {
match self {
BrainError::Server {
retry_after_ms: Some(ms),
..
} => Some(Duration::from_millis(u64::from(*ms))),
_ => None,
}
}
}