use std::fmt;
use thiserror::Error;
#[derive(Debug, Clone)]
pub struct ErrorLocation {
pub file: &'static str,
pub line: u32,
pub column: Option<u32>,
pub function: Option<&'static str>,
}
impl ErrorLocation {
#[must_use]
#[inline]
pub const fn new(file: &'static str, line: u32) -> Self {
Self {
file,
line,
column: None,
function: None,
}
}
#[must_use]
#[inline]
pub const fn new_with_function(file: &'static str, line: u32, function: &'static str) -> Self {
Self {
file,
line,
column: None,
function: Some(function),
}
}
#[must_use]
#[inline]
pub const fn new_with_column(file: &'static str, line: u32, column: u32) -> Self {
Self {
file,
line,
column: Some(column),
function: None,
}
}
#[must_use]
#[inline]
pub const fn new_full(
file: &'static str,
line: u32,
column: u32,
function: &'static str,
) -> Self {
Self {
file,
line,
column: Some(column),
function: Some(function),
}
}
#[must_use]
#[inline]
pub fn here() -> Self {
Self::new(file!(), line!())
}
}
impl fmt::Display for ErrorLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.file, self.line)?;
if let Some(column) = self.column {
write!(f, ":{column}")?;
}
if let Some(function) = self.function {
write!(f, " in {function}")?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct ErrorContext {
pub message: String,
pub location: Option<ErrorLocation>,
pub cause: Option<Box<CoreError>>,
}
impl ErrorContext {
#[must_use]
pub fn new<S: Into<String>>(message: S) -> Self {
Self {
message: message.into(),
location: None,
cause: None,
}
}
#[must_use]
pub fn with_location(mut self, location: ErrorLocation) -> Self {
self.location = Some(location);
self
}
#[must_use]
pub fn with_cause(mut self, cause: CoreError) -> Self {
self.cause = Some(Box::new(cause));
self
}
}
impl fmt::Display for ErrorContext {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message)?;
if let Some(location) = &self.location {
write!(f, " at {location}")?;
}
if let Some(cause) = &self.cause {
write!(f, "\nCaused by: {cause}")?;
}
Ok(())
}
}
#[derive(Error, Debug, Clone)]
pub enum CoreError {
#[error("{0}")]
ComputationError(ErrorContext),
#[error("{0}")]
DomainError(ErrorContext),
#[error("{0}")]
DispatchError(ErrorContext),
#[error("{0}")]
ConvergenceError(ErrorContext),
#[error("{0}")]
DimensionError(ErrorContext),
#[error("{0}")]
ShapeError(ErrorContext),
#[error("{0}")]
IndexError(ErrorContext),
#[error("{0}")]
ValueError(ErrorContext),
#[error("{0}")]
TypeError(ErrorContext),
#[error("{0}")]
NotImplementedError(ErrorContext),
#[error("{0}")]
ImplementationError(ErrorContext),
#[error("{0}")]
MemoryError(ErrorContext),
#[error("{0}")]
AllocationError(ErrorContext),
#[error("{0}")]
ConfigError(ErrorContext),
#[error("{0}")]
InvalidArgument(ErrorContext),
#[error("{0}")]
InvalidInput(ErrorContext),
#[error("{0}")]
PermissionError(ErrorContext),
#[error("{0}")]
ValidationError(ErrorContext),
#[error("{0}")]
InvalidState(ErrorContext),
#[error("{0}")]
JITError(ErrorContext),
#[error("JSON error: {0}")]
JSONError(ErrorContext),
#[error("IO error: {0}")]
IoError(ErrorContext),
#[error("Scheduler error: {0}")]
SchedulerError(ErrorContext),
#[error("Timeout error: {0}")]
TimeoutError(ErrorContext),
#[error("Compression error: {0}")]
CompressionError(ErrorContext),
#[error("Invalid shape: {0}")]
InvalidShape(ErrorContext),
#[error("Device error: {0}")]
DeviceError(ErrorContext),
#[error("Mutex error: {0}")]
MutexError(ErrorContext),
#[error("Thread error: {0}")]
ThreadError(ErrorContext),
#[error("Stream error: {0}")]
StreamError(ErrorContext),
#[error("End of stream: {0}")]
EndOfStream(ErrorContext),
#[error("Resource error: {0}")]
ResourceError(ErrorContext),
#[error("Communication error: {0}")]
CommunicationError(ErrorContext),
#[error("Security error: {0}")]
SecurityError(ErrorContext),
}
pub type CoreResult<T> = Result<T, CoreError>;
impl From<std::io::Error> for CoreError {
fn from(err: std::io::Error) -> Self {
CoreError::IoError(ErrorContext::new(format!("IO error: {err}")))
}
}
#[cfg(feature = "serialization")]
impl From<serde_json::Error> for CoreError {
fn from(err: serde_json::Error) -> Self {
CoreError::JSONError(ErrorContext::new(format!("JSON error: {err}")))
}
}
impl From<String> for CoreError {
fn from(err: String) -> Self {
CoreError::ValueError(ErrorContext::new(err))
}
}
impl From<crate::array_protocol::OperationError> for CoreError {
fn from(err: crate::array_protocol::OperationError) -> Self {
use crate::array_protocol::OperationError;
match err {
OperationError::NotImplemented(msg) => {
CoreError::NotImplementedError(ErrorContext::new(msg))
}
OperationError::ShapeMismatch(msg) => CoreError::ShapeError(ErrorContext::new(msg)),
OperationError::TypeMismatch(msg) => CoreError::TypeError(ErrorContext::new(msg)),
OperationError::Other(msg) => CoreError::ComputationError(ErrorContext::new(msg)),
}
}
}
#[macro_export]
macro_rules! error_context {
($message:expr) => {
$crate::error::ErrorContext::new($message)
.with_location($crate::error::ErrorLocation::new(file!(), line!()))
};
($message:expr, $function:expr) => {
$crate::error::ErrorContext::new($message).with_location(
$crate::error::ErrorLocation::new_with_function(file!(), line!(), $function),
)
};
}
#[macro_export]
macro_rules! domainerror {
($message:expr) => {
$crate::error::CoreError::DomainError(error_context!($message))
};
($message:expr, $function:expr) => {
$crate::error::CoreError::DomainError(error_context!($message, $function))
};
}
#[macro_export]
macro_rules! dimensionerror {
($message:expr) => {
$crate::error::CoreError::DimensionError(error_context!($message))
};
($message:expr, $function:expr) => {
$crate::error::CoreError::DimensionError(error_context!($message, $function))
};
}
#[macro_export]
macro_rules! valueerror {
($message:expr) => {
$crate::error::CoreError::ValueError(error_context!($message))
};
($message:expr, $function:expr) => {
$crate::error::CoreError::ValueError(error_context!($message, $function))
};
}
#[macro_export]
macro_rules! computationerror {
($message:expr) => {
$crate::error::CoreError::ComputationError(error_context!($message))
};
($message:expr, $function:expr) => {
$crate::error::CoreError::ComputationError(error_context!($message, $function))
};
}
#[allow(dead_code)]
pub fn check_domain<S: Into<String>>(condition: bool, message: S) -> CoreResult<()> {
if condition {
Ok(())
} else {
Err(CoreError::DomainError(
ErrorContext::new(message).with_location(ErrorLocation::new(file!(), line!())),
))
}
}
#[allow(dead_code)]
pub fn check_dimensions<S: Into<String>>(condition: bool, message: S) -> CoreResult<()> {
if condition {
Ok(())
} else {
Err(CoreError::DimensionError(
ErrorContext::new(message).with_location(ErrorLocation::new(file!(), line!())),
))
}
}
#[allow(dead_code)]
pub fn check_value<S: Into<String>>(condition: bool, message: S) -> CoreResult<()> {
if condition {
Ok(())
} else {
Err(CoreError::ValueError(
ErrorContext::new(message).with_location(ErrorLocation::new(file!(), line!())),
))
}
}
#[allow(dead_code)]
pub fn validate<T, F, S>(value: T, validator: F, message: S) -> CoreResult<T>
where
F: FnOnce(&T) -> bool,
S: Into<String>,
{
if validator(&value) {
Ok(value)
} else {
Err(CoreError::ValidationError(
ErrorContext::new(message).with_location(ErrorLocation::new(file!(), line!())),
))
}
}
#[must_use]
#[allow(dead_code)]
pub fn converterror<E, S>(error: E, message: S) -> CoreError
where
E: std::error::Error + 'static,
S: Into<String>,
{
let message_str = message.into();
let error_message = format!("{message_str} | Original error: {error}");
CoreError::ComputationError(
ErrorContext::new(error_message).with_location(ErrorLocation::new(file!(), line!())),
)
}
#[must_use]
#[allow(dead_code)]
pub fn chainerror<S>(error: CoreError, message: S) -> CoreError
where
S: Into<String>,
{
CoreError::ComputationError(
ErrorContext::new(message)
.with_location(ErrorLocation::new(file!(), line!()))
.with_cause(error),
)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorRecoveryStrategy {
FailFast,
RetryExponential,
RetryLinear,
Fallback,
Skip,
UseDefault,
LogAndContinue,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ErrorSeverity {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone)]
pub struct ErrorHandlerConfig {
pub max_retries: usize,
pub initial_delay_ms: u64,
pub max_delay_ms: u64,
pub log_errors: bool,
pub recovery_strategy: ErrorRecoveryStrategy,
pub min_severity: ErrorSeverity,
}
impl Default for ErrorHandlerConfig {
fn default() -> Self {
Self {
max_retries: 3,
initial_delay_ms: 100,
max_delay_ms: 5000,
log_errors: true,
recovery_strategy: ErrorRecoveryStrategy::FailFast,
min_severity: ErrorSeverity::Low,
}
}
}
#[derive(Debug, Clone)]
pub struct EnhancedError {
pub error: CoreError,
pub severity: ErrorSeverity,
pub recovery_strategy: ErrorRecoveryStrategy,
pub timestamp: std::time::SystemTime,
pub category: Option<String>,
pub tags: Vec<String>,
}
impl EnhancedError {
pub fn new(error: CoreError, severity: ErrorSeverity) -> Self {
Self {
error,
severity,
recovery_strategy: ErrorRecoveryStrategy::FailFast,
timestamp: std::time::SystemTime::now(),
category: None,
tags: Vec::new(),
}
}
pub fn with_recovery(mut self, strategy: ErrorRecoveryStrategy) -> Self {
self.recovery_strategy = strategy;
self
}
pub fn with_category(mut self, category: impl Into<String>) -> Self {
self.category = Some(category.into());
self
}
pub fn with_tag(mut self, tag: impl Into<String>) -> Self {
self.tags.push(tag.into());
self
}
pub fn has_tag(&self, tag: &str) -> bool {
self.tags.iter().any(|t| t == tag)
}
pub fn is_retryable(&self) -> bool {
matches!(
self.recovery_strategy,
ErrorRecoveryStrategy::RetryExponential | ErrorRecoveryStrategy::RetryLinear
)
}
pub fn is_recoverable(&self) -> bool {
!matches!(self.recovery_strategy, ErrorRecoveryStrategy::FailFast)
}
}
impl fmt::Display for EnhancedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[{:?}] {}", self.severity, self.error)?;
if let Some(category) = &self.category {
write!(f, " (category: {category})")?;
}
if !self.tags.is_empty() {
write!(f, " [tags: {}]", self.tags.join(", "))?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct BatchError {
pub errors: Vec<EnhancedError>,
pub total_operations: usize,
pub successful_operations: usize,
}
impl BatchError {
pub fn new(total_operations: usize) -> Self {
Self {
errors: Vec::new(),
total_operations,
successful_operations: 0,
}
}
pub fn add_error(&mut self, error: EnhancedError) {
self.errors.push(error);
}
pub fn add_success(&mut self) {
self.successful_operations += 1;
}
pub fn error_rate(&self) -> f64 {
if self.total_operations == 0 {
0.0
} else {
self.errors.len() as f64 / self.total_operations as f64
}
}
pub fn success_rate(&self) -> f64 {
if self.total_operations == 0 {
0.0
} else {
self.successful_operations as f64 / self.total_operations as f64
}
}
pub fn is_successful(&self, min_success_rate: f64) -> bool {
self.success_rate() >= min_success_rate
}
pub fn errors_by_severity(&self, severity: ErrorSeverity) -> Vec<&EnhancedError> {
self.errors
.iter()
.filter(|e| e.severity == severity)
.collect()
}
pub fn errors_by_category(&self, category: &str) -> Vec<&EnhancedError> {
self.errors
.iter()
.filter(|e| e.category.as_deref() == Some(category))
.collect()
}
pub fn retryable_errors(&self) -> Vec<&EnhancedError> {
self.errors.iter().filter(|e| e.is_retryable()).collect()
}
}
impl fmt::Display for BatchError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Batch operation: {}/{} successful ({:.1}% success rate)",
self.successful_operations,
self.total_operations,
self.success_rate() * 100.0
)?;
if !self.errors.is_empty() {
write!(f, "\nErrors ({})", self.errors.len())?;
for (i, error) in self.errors.iter().enumerate() {
write!(f, "\n {}: {}", i + 1, error)?;
}
}
Ok(())
}
}
pub type EnhancedResult<T> = Result<T, EnhancedError>;
pub type BatchResult<T> = Result<T, BatchError>;
pub fn enhance_error(error: CoreError, severity: ErrorSeverity) -> EnhancedError {
EnhancedError::new(error, severity)
}
pub fn critical_error(error: CoreError) -> EnhancedError {
EnhancedError::new(error, ErrorSeverity::Critical)
}
pub fn high_error(error: CoreError) -> EnhancedError {
EnhancedError::new(error, ErrorSeverity::High)
}
pub fn medium_error(error: CoreError) -> EnhancedError {
EnhancedError::new(error, ErrorSeverity::Medium)
}
pub fn low_error(error: CoreError) -> EnhancedError {
EnhancedError::new(error, ErrorSeverity::Low)
}
#[macro_export]
macro_rules! enhanced_error {
(critical, $error:expr) => {
$crate::error::critical_error($error)
};
(high, $error:expr) => {
$crate::error::high_error($error)
};
(medium, $error:expr) => {
$crate::error::medium_error($error)
};
(low, $error:expr) => {
$crate::error::low_error($error)
};
($error:expr) => {
$crate::error::medium_error($error)
};
}
#[macro_export]
macro_rules! batch_operation {
($total:expr, $operations:block) => {{
let mut batch_error = $crate::error::BatchError::new($total);
let result = $operations;
if batch_error.errors.is_empty() {
Ok(result)
} else {
Err(batch_error)
}
}};
}
#[macro_export]
macro_rules! with_retry {
($operation:expr, $max_retries:expr) => {{
let mut attempts = 0;
let mut last_error = None;
loop {
match $operation {
Ok(result) => break Ok(result),
Err(error) => {
attempts += 1;
last_error = Some(error);
if attempts >= $max_retries {
break Err(last_error.expect("Operation failed"));
}
std::thread::sleep(std::time::Duration::from_millis(attempts * 100));
}
}
}
}};
}