Skip to main content

scirs2/
error.rs

1//! Error handling for scirs2-python
2//!
3//! This module provides comprehensive error types and conversions for Python exceptions.
4
5use pyo3::exceptions::{PyIndexError, PyRuntimeError, PyTypeError, PyValueError};
6use pyo3::prelude::*;
7use std::fmt;
8
9/// Main error type for scirs2-python operations
10#[derive(Debug)]
11pub enum SciRS2Error {
12    /// Array operation error (dimension mismatch, invalid shape, etc.)
13    ArrayError(String),
14    /// Numerical computation error (NaN, infinity, convergence failure)
15    ComputationError(String),
16    /// Invalid input parameters
17    ValueError(String),
18    /// Type conversion error
19    TypeError(String),
20    /// Index out of bounds
21    IndexError(String),
22    /// Memory allocation error
23    MemoryError(String),
24    /// Generic runtime error
25    RuntimeError(String),
26}
27
28impl fmt::Display for SciRS2Error {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        match self {
31            Self::ArrayError(msg) => write!(f, "Array error: {}", msg),
32            Self::ComputationError(msg) => write!(f, "Computation error: {}", msg),
33            Self::ValueError(msg) => write!(f, "Value error: {}", msg),
34            Self::TypeError(msg) => write!(f, "Type error: {}", msg),
35            Self::IndexError(msg) => write!(f, "Index error: {}", msg),
36            Self::MemoryError(msg) => write!(f, "Memory error: {}", msg),
37            Self::RuntimeError(msg) => write!(f, "Runtime error: {}", msg),
38        }
39    }
40}
41
42impl std::error::Error for SciRS2Error {}
43
44impl From<SciRS2Error> for PyErr {
45    fn from(err: SciRS2Error) -> PyErr {
46        match err {
47            SciRS2Error::ArrayError(msg)
48            | SciRS2Error::ComputationError(msg)
49            | SciRS2Error::RuntimeError(msg) => PyRuntimeError::new_err(msg),
50            SciRS2Error::ValueError(msg) => PyValueError::new_err(msg),
51            SciRS2Error::TypeError(msg) => PyTypeError::new_err(msg),
52            SciRS2Error::IndexError(msg) => PyIndexError::new_err(msg),
53            SciRS2Error::MemoryError(msg) => {
54                PyRuntimeError::new_err(format!("Memory error: {}", msg))
55            }
56        }
57    }
58}
59
60/// Helper trait for converting numpy array access errors
61pub trait ArrayAccessExt<T> {
62    fn to_scirs2_err(self, context: &str) -> Result<T, SciRS2Error>;
63}
64
65impl<T> ArrayAccessExt<T> for Option<T> {
66    fn to_scirs2_err(self, context: &str) -> Result<T, SciRS2Error> {
67        self.ok_or_else(|| SciRS2Error::ArrayError(format!("{}: array access failed", context)))
68    }
69}
70
71/// Helper function to safely get array slice with context
72pub fn get_array_slice<'a, T>(
73    arr: &'a ndarray::ArrayView1<'_, T>,
74    context: &str,
75) -> Result<&'a [T], SciRS2Error>
76where
77    T: ndarray::NdFloat,
78{
79    arr.as_slice()
80        .ok_or_else(|| SciRS2Error::ArrayError(format!("{}: array is not contiguous", context)))
81}
82
83/// Helper function to safely get 2D array slice with context
84pub fn get_array_slice_2d<'a, T>(
85    arr: &'a ndarray::ArrayView2<'_, T>,
86    context: &str,
87) -> Result<&'a [T], SciRS2Error>
88where
89    T: ndarray::NdFloat,
90{
91    arr.as_slice().ok_or_else(|| {
92        SciRS2Error::ArrayError(format!(
93            "{}: array is not contiguous or not in standard layout",
94            context
95        ))
96    })
97}
98
99/// Helper function to validate array is not empty
100pub fn check_not_empty<T>(
101    arr: &ndarray::ArrayView1<'_, T>,
102    operation: &str,
103) -> Result<(), SciRS2Error> {
104    if arr.is_empty() {
105        Err(SciRS2Error::ValueError(format!(
106            "{}: array must not be empty",
107            operation
108        )))
109    } else {
110        Ok(())
111    }
112}
113
114/// Helper function to validate arrays have same length
115pub fn check_same_length<T, U>(
116    arr1: &ndarray::ArrayView1<'_, T>,
117    arr2: &ndarray::ArrayView1<'_, U>,
118    operation: &str,
119) -> Result<(), SciRS2Error> {
120    if arr1.len() != arr2.len() {
121        Err(SciRS2Error::ValueError(format!(
122            "{}: arrays must have same length (got {} and {})",
123            operation,
124            arr1.len(),
125            arr2.len()
126        )))
127    } else {
128        Ok(())
129    }
130}
131
132/// Helper function to validate minimum array length
133pub fn check_min_length<T>(
134    arr: &ndarray::ArrayView1<'_, T>,
135    min_len: usize,
136    operation: &str,
137) -> Result<(), SciRS2Error> {
138    if arr.len() < min_len {
139        Err(SciRS2Error::ValueError(format!(
140            "{}: array must have at least {} elements (got {})",
141            operation,
142            min_len,
143            arr.len()
144        )))
145    } else {
146        Ok(())
147    }
148}