use serde::{Deserialize, Serialize};
use thiserror::Error;
pub type Result<T> = std::result::Result<T, CalculationError>;
#[derive(Error, Debug, Serialize, Deserialize)]
pub enum CalculationError {
#[error("Validation error: {field} - {message}")]
ValidationError { field: String, message: String },
#[error("Invalid input: {0}")]
InvalidInput(String),
#[error("Calculation failed: {0}")]
CalculationFailed(String),
#[error("JSON parse error: {0}")]
JsonParseError(String),
#[error("Configuration error: {0}")]
ConfigError(String),
#[error("Internal error: {0}")]
InternalError(String),
}
impl CalculationError {
pub fn to_json(&self) -> String {
serde_json::to_string_pretty(self).unwrap_or_default()
}
pub fn validation(field: &str, message: &str) -> Self {
Self::ValidationError {
field: field.to_string(),
message: message.to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationError {
pub field: String,
pub message: String,
}
impl ValidationError {
pub fn new(field: &str, message: &str) -> Self {
Self {
field: field.to_string(),
message: message.to_string(),
}
}
}
impl From<ValidationError> for CalculationError {
fn from(err: ValidationError) -> Self {
CalculationError::validation(&err.field, &err.message)
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ApiError {
pub success: bool,
pub error: ApiErrorDetail,
pub timestamp: String,
pub request_id: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ApiErrorDetail {
pub code: String,
pub message: String,
pub details: Option<serde_json::Value>,
}
impl From<CalculationError> for ApiError {
fn from(err: CalculationError) -> Self {
let (code, message) = match &err {
CalculationError::ValidationError { .. } => ("VALIDATION_ERROR", err.to_string()),
CalculationError::InvalidInput(_) => ("INVALID_INPUT", err.to_string()),
CalculationError::CalculationFailed(_) => ("CALCULATION_FAILED", err.to_string()),
CalculationError::JsonParseError(_) => ("JSON_PARSE_ERROR", err.to_string()),
CalculationError::ConfigError(_) => ("CONFIG_ERROR", err.to_string()),
CalculationError::InternalError(_) => ("INTERNAL_ERROR", err.to_string()),
};
Self {
success: false,
error: ApiErrorDetail {
code: code.to_string(),
message,
details: serde_json::to_value(err).ok(),
},
timestamp: chrono::Utc::now().to_rfc3339(),
request_id: None,
}
}
}