use thiserror::Error;
pub type Result<T> = std::result::Result<T, ConnectorError>;
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum ConnectorError {
#[error("Connection error: {0}")]
ConnectionError(String),
#[error("Not connected to Strike48 server")]
NotConnected,
#[error("Connector not registered")]
NotRegistered,
#[error("Stream error: {0}")]
StreamError(String),
#[error("Registration error: {0}")]
RegistrationError(String),
#[error("Invalid configuration: {0}")]
InvalidConfig(String),
#[error("Serialization error: {0}")]
SerializationError(String),
#[error("Deserialization error: {0}")]
DeserializationError(String),
#[error("Unsupported encoding: {0}")]
UnsupportedEncoding(String),
#[error("Timeout: {0}")]
Timeout(String),
#[error("Invoke failed: {0}")]
InvokeFailed(String),
#[error("Connector not running")]
NotRunning,
#[error("Already running")]
AlreadyRunning,
#[error("IO error: {0}")]
Io(#[source] Box<std::io::Error>),
#[error("gRPC error: {0}")]
Grpc(#[source] Box<tonic::Status>),
#[error("JSON error: {0}")]
Json(#[source] Box<serde_json::Error>),
#[error("Not implemented: {0}")]
NotImplemented(String),
#[error("Other error: {0}")]
Other(String),
}
impl From<std::io::Error> for ConnectorError {
fn from(e: std::io::Error) -> Self {
ConnectorError::Io(Box::new(e))
}
}
impl From<tonic::Status> for ConnectorError {
fn from(e: tonic::Status) -> Self {
ConnectorError::Grpc(Box::new(e))
}
}
impl From<serde_json::Error> for ConnectorError {
fn from(e: serde_json::Error) -> Self {
ConnectorError::Json(Box::new(e))
}
}
impl ConnectorError {
pub fn code(&self) -> &'static str {
match self {
ConnectorError::ConnectionError(_) => "CONNECTION_ERROR",
ConnectorError::NotConnected => "NOT_CONNECTED",
ConnectorError::NotRegistered => "NOT_REGISTERED",
ConnectorError::StreamError(_) => "STREAM_ERROR",
ConnectorError::RegistrationError(_) => "REGISTRATION_ERROR",
ConnectorError::InvalidConfig(_) => "INVALID_CONFIG",
ConnectorError::SerializationError(_) => "SERIALIZATION_ERROR",
ConnectorError::DeserializationError(_) => "DESERIALIZATION_ERROR",
ConnectorError::UnsupportedEncoding(_) => "UNSUPPORTED_ENCODING",
ConnectorError::Timeout(_) => "TIMEOUT",
ConnectorError::InvokeFailed(_) => "INVOKE_FAILED",
ConnectorError::NotRunning => "NOT_RUNNING",
ConnectorError::AlreadyRunning => "ALREADY_RUNNING",
ConnectorError::Io(_) => "IO_ERROR",
ConnectorError::Grpc(_) => "GRPC_ERROR",
ConnectorError::Json(_) => "JSON_ERROR",
ConnectorError::NotImplemented(_) => "NOT_IMPLEMENTED",
ConnectorError::Other(_) => "OTHER",
}
}
pub fn is_recoverable(&self) -> bool {
match self {
ConnectorError::ConnectionError(_)
| ConnectorError::NotConnected
| ConnectorError::StreamError(_)
| ConnectorError::Timeout(_)
| ConnectorError::Io(_)
| ConnectorError::Grpc(_) => true,
ConnectorError::RegistrationError(msg) => {
let lower = msg.to_lowercase();
let permanent = lower.contains("not enabled")
|| lower.contains("not supported")
|| lower.contains("disabled")
|| lower.contains("invalid connector")
|| lower.contains("permission denied")
|| lower.contains("not allowed");
!permanent
}
ConnectorError::InvalidConfig(_)
| ConnectorError::NotRegistered
| ConnectorError::SerializationError(_)
| ConnectorError::DeserializationError(_)
| ConnectorError::UnsupportedEncoding(_)
| ConnectorError::NotImplemented(_)
| ConnectorError::NotRunning
| ConnectorError::AlreadyRunning
| ConnectorError::Json(_) => false,
ConnectorError::InvokeFailed(_) | ConnectorError::Other(_) => true,
}
}
}