use serde_json::Value;
use std::fmt;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum RunAgentError {
#[error("Authentication error: {message}")]
Authentication { message: String },
#[error("Validation error: {message}")]
Validation { message: String },
#[error("Connection error: {message}")]
Connection { message: String },
#[error("Server error: {message}")]
Server { message: String },
#[error("Template error: {message}")]
Template { message: String },
#[error("Deployment error: {message}")]
Deployment { message: String },
#[error("Database error: {message}")]
Database { message: String },
#[error("Configuration error: {message}")]
Config { message: String },
#[error("{code}: {message}")]
Execution {
code: String,
message: String,
suggestion: Option<String>,
details: Option<Value>,
},
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("HTTP error: {0}")]
Http(#[from] reqwest::Error),
#[error("RunAgent error: {message}")]
Generic { message: String },
}
impl RunAgentError {
pub fn authentication<S: Into<String>>(message: S) -> Self {
Self::Authentication {
message: message.into(),
}
}
pub fn validation<S: Into<String>>(message: S) -> Self {
Self::Validation {
message: message.into(),
}
}
pub fn connection<S: Into<String>>(message: S) -> Self {
Self::Connection {
message: message.into(),
}
}
pub fn server<S: Into<String>>(message: S) -> Self {
Self::Server {
message: message.into(),
}
}
pub fn template<S: Into<String>>(message: S) -> Self {
Self::Template {
message: message.into(),
}
}
pub fn deployment<S: Into<String>>(message: S) -> Self {
Self::Deployment {
message: message.into(),
}
}
pub fn database<S: Into<String>>(message: S) -> Self {
Self::Database {
message: message.into(),
}
}
pub fn config<S: Into<String>>(message: S) -> Self {
Self::Config {
message: message.into(),
}
}
pub fn execution<S: Into<String>>(
code: S,
message: S,
suggestion: Option<String>,
details: Option<Value>,
) -> Self {
Self::Execution {
code: code.into(),
message: message.into(),
suggestion,
details,
}
}
pub fn generic<S: Into<String>>(message: S) -> Self {
Self::Generic {
message: message.into(),
}
}
pub fn category(&self) -> &'static str {
match self {
Self::Authentication { .. } => "authentication",
Self::Validation { .. } => "validation",
Self::Connection { .. } => "connection",
Self::Server { .. } => "server",
Self::Template { .. } => "template",
Self::Deployment { .. } => "deployment",
Self::Database { .. } => "database",
Self::Config { .. } => "config",
Self::Execution { .. } => "execution",
Self::Io(_) => "io",
Self::Json(_) => "json",
Self::Http(_) => "http",
Self::Generic { .. } => "generic",
}
}
pub fn is_retryable(&self) -> bool {
matches!(
self,
Self::Connection { .. } | Self::Server { .. } | Self::Http(_)
) || matches!(self, Self::Execution { code, .. } if code == "CONNECTION_ERROR" || code == "SERVER_ERROR")
}
}
pub type RunAgentResult<T> = Result<T, RunAgentError>;
#[derive(Debug, Clone)]
pub struct HttpErrorDetails {
pub status_code: Option<u16>,
pub headers: std::collections::HashMap<String, String>,
pub body: Option<String>,
}
impl fmt::Display for HttpErrorDetails {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "HTTP Error")?;
if let Some(status) = self.status_code {
write!(f, " {}", status)?;
}
if let Some(body) = &self.body {
write!(f, ": {}", body)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_creation() {
let err = RunAgentError::authentication("Invalid API key");
assert_eq!(err.category(), "authentication");
assert!(!err.is_retryable());
}
#[test]
fn test_error_categories() {
let validation_err = RunAgentError::validation("Invalid input");
assert_eq!(validation_err.category(), "validation");
let connection_err = RunAgentError::connection("Network timeout");
assert_eq!(connection_err.category(), "connection");
assert!(connection_err.is_retryable());
}
#[test]
fn test_error_display() {
let err = RunAgentError::server("Internal server error");
let error_string = format!("{}", err);
assert!(error_string.contains("Server error"));
assert!(error_string.contains("Internal server error"));
}
}