1use pyo3::exceptions::{PyIndexError, PyRuntimeError, PyTypeError, PyValueError};
6use pyo3::prelude::*;
7use std::fmt;
8
9#[derive(Debug)]
11pub enum SciRS2Error {
12 ArrayError(String),
14 ComputationError(String),
16 ValueError(String),
18 TypeError(String),
20 IndexError(String),
22 MemoryError(String),
24 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
60pub 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
71pub 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
83pub 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
99pub 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
114pub 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
132pub 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}