use std::path::PathBuf;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Mecha10Error {
#[error("Node initialization failed: {0}")]
NodeInit(String),
#[error("Node '{node_name}' failed: {message}")]
NodeFailed { node_name: String, message: String },
#[error("Node '{0}' not found")]
NodeNotFound(String),
#[error("Configuration error in field '{field}': {message}\n Expected: {expected}\n Got: {got}\n Suggestion: {suggestion}")]
ConfigValidation {
field: String,
message: String,
expected: String,
got: String,
suggestion: String,
},
#[error(
"Configuration file not found: {path}\n Suggestion: Create the file or set environment variable {env_var}"
)]
ConfigNotFound { path: PathBuf, env_var: String },
#[error("Invalid configuration format: {0}\n Suggestion: Validate against schema at {1}")]
ConfigInvalid(String, String),
#[error("Configuration error: {0}")]
Configuration(String),
#[error("{message}\n Suggestion: {suggestion}")]
MessagingError { message: String, suggestion: String },
#[error("Failed to connect to message broker at {url}\n → Tried: {url}\n → Suggestion: {suggestion}")]
MessagingConnectionFailed { url: String, suggestion: String },
#[error("Failed to publish message to topic '{topic}': {reason}")]
PublishFailed { topic: String, reason: String },
#[error("Failed to subscribe to topic '{topic}': {reason}")]
SubscribeFailed { topic: String, reason: String },
#[error("Message deserialization failed for topic '{topic}'\n Expected type: {expected_type}\n Error: {error}")]
DeserializationFailed {
topic: String,
expected_type: String,
error: String,
},
#[error("Message serialization failed: {0}")]
SerializationFailed(String),
#[error("Serialization error: {message}\n Suggestion: {suggestion}")]
SerializationError { message: String, suggestion: String },
#[error("Circuit breaker open for service '{service}': {message}")]
CircuitBreakerOpen { service: String, message: String },
#[error("Failed to load model from '{uri}': {reason}\n Suggestion: {suggestion}")]
ModelLoadFailed {
uri: String,
reason: String,
suggestion: String,
},
#[error("Model inference failed: {0}")]
InferenceFailed(String),
#[error(
"Model not found in MLflow registry: {model_name}/{version}\n Suggestion: Check MLflow UI at {mlflow_url}"
)]
ModelNotFound {
model_name: String,
version: String,
mlflow_url: String,
},
#[error("Model checksum mismatch\n Expected: {expected}\n Got: {got}\n Suggestion: Re-download the model")]
ModelChecksumMismatch { expected: String, got: String },
#[error("Build failed for node '{node_name}':\n{stderr}\n Suggestion: Check the error messages above")]
BuildFailed { node_name: String, stderr: String },
#[error("Hot-reload failed: {0}")]
HotReloadFailed(String),
#[error("Runtime error: {0}")]
Runtime(String),
#[error("Timeout after {timeout_ms}ms waiting for: {operation}")]
Timeout { timeout_ms: u64, operation: String },
#[error("Shutdown error: {0}")]
Shutdown(String),
#[error("File not found: {path}\n Suggestion: {suggestion}")]
FileNotFound { path: PathBuf, suggestion: String },
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("IO error: {message}\n Suggestion: {suggestion}")]
IoError { message: String, suggestion: String },
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("YAML error: {0}")]
Yaml(String),
#[error("Redis error: {0}")]
Redis(String),
#[error("Database error: {0}")]
Database(String),
#[error("HTTP error: {0}")]
Http(String),
#[error("{0}")]
Other(String),
}
pub type Result<T> = std::result::Result<T, Mecha10Error>;
impl Mecha10Error {
pub fn connection_failed(url: impl Into<String>) -> Self {
let url = url.into();
let suggestion = if url.contains("localhost") || url.contains("127.0.0.1") {
"Start Redis with `redis-server`\n → Or set REDIS_URL environment variable".to_string()
} else {
format!("Check network connection and ensure Redis is running at {}", url)
};
Self::MessagingConnectionFailed { url, suggestion }
}
pub fn config_validation(
field: impl Into<String>,
message: impl Into<String>,
expected: impl Into<String>,
got: impl Into<String>,
suggestion: impl Into<String>,
) -> Self {
Self::ConfigValidation {
field: field.into(),
message: message.into(),
expected: expected.into(),
got: got.into(),
suggestion: suggestion.into(),
}
}
pub fn model_load_failed(uri: impl Into<String>, reason: impl Into<String>) -> Self {
let uri_str = uri.into();
let reason = reason.into();
let suggestion = if uri_str.starts_with("mlflow://") {
"Check MLflow server is running with `mecha10 mlflow start`\n → Or verify MLFLOW_TRACKING_URI environment variable".to_string()
} else if uri_str.starts_with("http://") || uri_str.starts_with("https://") {
"Check network connection and model URL".to_string()
} else {
format!("Check file exists: {}", uri_str)
};
Self::ModelLoadFailed {
uri: uri_str,
reason,
suggestion,
}
}
pub fn messaging_error(message: impl Into<String>, suggestion: impl Into<String>) -> Self {
Self::MessagingError {
message: message.into(),
suggestion: suggestion.into(),
}
}
pub fn configuration(message: impl Into<String>) -> Self {
Self::Configuration(message.into())
}
pub fn file_not_found(path: impl Into<PathBuf>, suggestion: impl Into<String>) -> Self {
Self::FileNotFound {
path: path.into(),
suggestion: suggestion.into(),
}
}
pub fn config_not_found(path: impl Into<PathBuf>, env_var: impl Into<String>) -> Self {
Self::ConfigNotFound {
path: path.into(),
env_var: env_var.into(),
}
}
}
impl From<anyhow::Error> for Mecha10Error {
fn from(err: anyhow::Error) -> Self {
Self::Other(err.to_string())
}
}