Skip to main content

rstsr_common/
error.rs

1#[cfg(feature = "std")]
2extern crate std;
3
4extern crate alloc;
5
6use crate::prelude_dev::*;
7use alloc::collections::TryReserveError;
8use core::alloc::LayoutError;
9use core::convert::Infallible;
10use core::num::TryFromIntError;
11use derive_builder::UninitializedFieldError;
12
13#[non_exhaustive]
14#[derive(Debug)]
15pub enum RSTSRError {
16    ValueOutOfRange(String),
17    InvalidValue(String),
18    InvalidLayout(String),
19
20    /// An axis argument is out of bounds for a tensor's number of dimensions.
21    /// Structured (NumPy `AxisError`-like) so a caller may match and recover.
22    /// `axis` keeps the original, possibly-negative input the caller passed.
23    AxisError {
24        axis: isize,
25        ndim: usize,
26    },
27
28    /// An element index (or slice bound) is out of range along an axis.
29    /// Message-only (Python `IndexError`-like): a programmer bug, not recoverable.
30    IndexError(String),
31
32    RuntimeError(String),
33    DeviceMismatch(String),
34    UnImplemented(String),
35    MemoryError(String),
36
37    TryFromIntError(String),
38    Infallible,
39
40    BuilderError(UninitializedFieldError),
41    DeviceError(String),
42    RayonError(String),
43
44    ErrorCode(i32, String),
45    FaerError(String),
46
47    Miscellaneous(String),
48}
49
50#[cfg(feature = "backtrace")]
51#[derive(Debug)]
52pub struct RSTSRBacktrace(pub std::backtrace::Backtrace);
53#[cfg(not(feature = "backtrace"))]
54#[derive(Debug)]
55pub struct RSTSRBacktrace;
56
57#[cfg(feature = "backtrace")]
58impl core::fmt::Display for RSTSRBacktrace {
59    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
60        write!(f, "{:}", self.0)
61    }
62}
63
64#[cfg(not(feature = "backtrace"))]
65impl core::fmt::Display for RSTSRBacktrace {
66    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
67        write!(f, "Backtrace feature in RSTSR is disabled.")
68    }
69}
70
71#[derive(Debug)]
72pub struct Error {
73    pub inner: RSTSRError,
74    pub backtrace: Option<RSTSRBacktrace>,
75}
76
77pub fn rstsr_backtrace() -> Option<RSTSRBacktrace> {
78    #[cfg(feature = "backtrace")]
79    {
80        extern crate std;
81        let bt = std::backtrace::Backtrace::capture();
82        Some(RSTSRBacktrace(bt))
83    }
84    #[cfg(not(feature = "backtrace"))]
85    {
86        None
87    }
88}
89
90impl core::fmt::Display for Error {
91    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
92        Debug::fmt(self, f)
93    }
94}
95
96#[cfg(feature = "std")]
97impl std::error::Error for Error {}
98
99pub type Result<T> = core::result::Result<T, Error>;
100
101pub trait RSTSRResultAPI<T> {
102    fn rstsr_unwrap(self) -> T;
103}
104
105impl<T> RSTSRResultAPI<T> for Result<T> {
106    #[allow(unused_variables)]
107    fn rstsr_unwrap(self) -> T {
108        match self {
109            Ok(v) => v,
110            Err(e) => {
111                let Error { inner, backtrace } = &e;
112                #[cfg(feature = "backtrace")]
113                {
114                    extern crate std;
115                    if let Some(backtrace) = backtrace {
116                        std::eprintln!("\n====== RSTSR Backtrace ======\n{:}", backtrace);
117                    }
118                    panic!("RSTSR Error: {:?}", inner)
119                }
120                #[cfg(not(feature = "backtrace"))]
121                {
122                    panic!("RSTSR Error (backtrace disabled): {:?}", inner)
123                }
124            },
125        }
126    }
127}
128
129impl From<TryFromIntError> for Error {
130    fn from(e: TryFromIntError) -> Self {
131        Error { inner: RSTSRError::TryFromIntError(format!("{e:?}")), backtrace: rstsr_backtrace() }
132    }
133}
134
135impl From<Infallible> for Error {
136    fn from(_: Infallible) -> Self {
137        Error { inner: RSTSRError::Infallible, backtrace: rstsr_backtrace() }
138    }
139}
140
141#[cfg(feature = "rayon")]
142impl From<rayon::ThreadPoolBuildError> for Error {
143    fn from(e: rayon::ThreadPoolBuildError) -> Self {
144        Error { inner: RSTSRError::RayonError(format!("{e:?}")), backtrace: rstsr_backtrace() }
145    }
146}
147
148impl From<UninitializedFieldError> for Error {
149    fn from(e: UninitializedFieldError) -> Self {
150        Error { inner: RSTSRError::BuilderError(e), backtrace: rstsr_backtrace() }
151    }
152}
153
154impl From<TryReserveError> for Error {
155    fn from(e: TryReserveError) -> Self {
156        Error { inner: RSTSRError::MemoryError(format!("{e:?}")), backtrace: rstsr_backtrace() }
157    }
158}
159
160impl From<LayoutError> for Error {
161    fn from(e: LayoutError) -> Self {
162        Error { inner: RSTSRError::MemoryError(format!("{e:?}")), backtrace: rstsr_backtrace() }
163    }
164}
165
166#[macro_export]
167macro_rules! backtrace {
168    () => {{
169        #[cfg(feature = "backtrace")]
170        {
171            extern crate std;
172            let bt = std::backtrace::Backtrace::capture();
173            format!("\nBacktrace:\n{:}", bt)
174        }
175        #[cfg(not(feature = "backtrace"))]
176        {
177            String::new()
178        }
179    }};
180}
181
182#[macro_export]
183macro_rules! rstsr_assert {
184    ($cond:expr, $errtype:ident) => {
185        if $cond {
186            Ok(())
187        } else {
188            use $crate::prelude_dev::*;
189            let mut s = String::new();
190            write!(s, concat!(file!(), ":", line!(), ": ")).unwrap();
191            write!(s, concat!("Error::", stringify!($errtype))).unwrap();
192            write!(s, " : {:}", stringify!($cond)).unwrap();
193            Err(Error{ inner: RSTSRError::$errtype(s), backtrace: rstsr_backtrace() })
194        }
195    };
196    ($cond:expr, $errtype:ident, $($arg:tt)*) => {{
197        if $cond {
198            Ok(())
199        } else {
200            use $crate::prelude_dev::*;
201            let mut s = String::new();
202            write!(s, concat!(file!(), ":", line!(), ": ")).unwrap();
203            write!(s, concat!("Error::", stringify!($errtype))).unwrap();
204            write!(s, " : ").unwrap();
205            write!(s, $($arg)*).unwrap();
206            write!(s, " : {:}", stringify!($cond)).unwrap();
207            Err(Error{ inner: RSTSRError::$errtype(s), backtrace: rstsr_backtrace() })
208        }
209    }};
210}
211
212#[macro_export]
213macro_rules! rstsr_assert_eq {
214    ($lhs:expr, $rhs:expr, $errtype:ident) => {
215        if $lhs == $rhs {
216            Ok(())
217        } else {
218            use $crate::prelude_dev::*;
219            let mut s = String::new();
220            write!(s, concat!(file!(), ":", line!(), ": ")).unwrap();
221            write!(s, concat!("Error::", stringify!($errtype))).unwrap();
222            write!(
223                s,
224                " : {:} = {:?} not equal to {:} = {:?}",
225                stringify!($lhs),
226                $lhs,
227                stringify!($rhs),
228                $rhs
229            )
230            .unwrap();
231            Err(Error{ inner: RSTSRError::$errtype(s), backtrace: rstsr_backtrace() })
232        }
233    };
234    ($lhs:expr, $rhs:expr, $errtype:ident, $($arg:tt)*) => {
235        if $lhs == $rhs {
236            Ok(())
237        } else {
238            use $crate::prelude_dev::*;
239            let mut s = String::new();
240            write!(s, concat!(file!(), ":", line!(), ": ")).unwrap();
241            write!(s, concat!("Error::", stringify!($errtype))).unwrap();
242            write!(s, " : ").unwrap();
243            write!(s, $($arg)*).unwrap();
244            write!(
245                s,
246                " : {:} = {:?} not equal to {:} = {:?}",
247                stringify!($lhs),
248                $lhs,
249                stringify!($rhs),
250                $rhs
251            )
252            .unwrap();
253            Err(Error{ inner: RSTSRError::$errtype(s), backtrace: rstsr_backtrace() })
254        }
255    };
256}
257
258#[macro_export]
259macro_rules! rstsr_invalid {
260    ($word:expr) => {{
261        use core::fmt::Write;
262        let mut s = String::new();
263        write!(s, concat!(file!(), ":", line!(), ": ")).unwrap();
264        write!(s, "Error::InvalidValue").unwrap();
265        write!(s, " : {:?} = {:?}", stringify!($word), $word).unwrap();
266        Err(Error{ inner: RSTSRError::InvalidValue(s), backtrace: rstsr_backtrace() })
267    }};
268    ($word:expr, $($arg:tt)*) => {{
269        use core::fmt::Write;
270        let mut s = String::new();
271        write!(s, concat!(file!(), ":", line!(), ": ")).unwrap();
272        write!(s, "Error::InvalidValue").unwrap();
273        write!(s, " : {:?} = {:?}", stringify!($word), $word).unwrap();
274        write!(s, " : ").unwrap();
275        write!(s, $($arg)*).unwrap();
276        Err(Error{ inner: RSTSRError::InvalidValue(s), backtrace: rstsr_backtrace() })
277    }};
278}
279
280#[macro_export]
281macro_rules! rstsr_errcode {
282    ($word:expr) => {{
283        use core::fmt::Write;
284        let mut s = String::new();
285        write!(s, concat!(file!(), ":", line!(), ": ")).unwrap();
286        write!(s, "Error::ErrorCode").unwrap();
287        write!(s, " : {:?}", $word).unwrap();
288        Err(Error{ inner: RSTSRError::ErrorCode($word, s), backtrace: rstsr_backtrace() })
289    }};
290    ($word:expr, $($arg:tt)*) => {{
291        use core::fmt::Write;
292        let mut s = String::new();
293        write!(s, concat!(file!(), ":", line!(), ": ")).unwrap();
294        write!(s, "Error::ErrorCode").unwrap();
295        write!(s, " : {:?}", $word).unwrap();
296        write!(s, " : ").unwrap();
297        write!(s, $($arg)*).unwrap();
298        Err(Error{ inner: RSTSRError::ErrorCode($word, s), backtrace: rstsr_backtrace() })
299    }};
300}
301
302#[macro_export]
303macro_rules! rstsr_error {
304    ($errtype:ident) => {{
305        use $crate::prelude_dev::*;
306        let mut s = String::new();
307        write!(s, concat!(file!(), ":", line!(), ": ")).unwrap();
308        write!(s, concat!("Error::", stringify!($errtype))).unwrap();
309        Error{ inner: RSTSRError::$errtype(s), backtrace: rstsr_backtrace() }
310    }};
311    ($errtype:ident, $($arg:tt)*) => {{
312        use $crate::prelude_dev::*;
313        let mut s = String::new();
314        write!(s, concat!(file!(), ":", line!(), ": ")).unwrap();
315        write!(s, concat!("Error::", stringify!($errtype))).unwrap();
316        write!(s, " : ").unwrap();
317        write!(s, $($arg)*).unwrap();
318        Error{ inner: RSTSRError::$errtype(s), backtrace: rstsr_backtrace() }
319    }};
320}
321
322#[macro_export]
323macro_rules! rstsr_raise {
324    ($errtype:ident) => {{
325        use $crate::prelude_dev::*;
326        let mut s = String::new();
327        write!(s, concat!(file!(), ":", line!(), ": ")).unwrap();
328        write!(s, concat!("Error::", stringify!($errtype))).unwrap();
329        Err(Error{ inner: RSTSRError::$errtype(s), backtrace: rstsr_backtrace() })
330    }};
331    ($errtype:ident, $($arg:tt)*) => {{
332        use $crate::prelude_dev::*;
333        let mut s = String::new();
334        write!(s, concat!(file!(), ":", line!(), ": ")).unwrap();
335        write!(s, concat!("Error::", stringify!($errtype))).unwrap();
336        write!(s, " : ").unwrap();
337        write!(s, $($arg)*).unwrap();
338        Err(Error{ inner: RSTSRError::$errtype(s), backtrace: rstsr_backtrace() })
339    }};
340}
341
342/// Validate an axis argument against a tensor's `ndim`, folding a negative axis
343/// (counted from the end, `-1` == last axis) before the bounds check.
344///
345/// Returns the normalized non-negative axis (`Ok(usize)`), or
346/// `Err(AxisError { axis: <original>, ndim })` when out of range. The `axis`
347/// field preserves the original (possibly negative) input so a caller can match
348/// and recover. Collapses the negative-fold-plus-bounds-check that was
349/// copy-pasted across the layout code.
350#[macro_export]
351macro_rules! rstsr_check_axis {
352    ($axis:expr, $ndim:expr) => {{
353        let axis: isize = $axis;
354        let ndim: usize = $ndim;
355        let norm = if axis < 0 { (ndim as isize) + axis } else { axis };
356        if norm >= 0 && (norm as usize) < ndim {
357            core::result::Result::Ok(norm as usize)
358        } else {
359            core::result::Result::Err($crate::error::Error {
360                inner: $crate::error::RSTSRError::AxisError { axis, ndim },
361                backtrace: $crate::error::rstsr_backtrace(),
362            })
363        }
364    }};
365}
366
367/// Like [`rstsr_check_axis!`], but the upper bound is `0..=ndim` (inclusive)
368/// rather than `0..ndim`. Used by operations that *insert* a new axis and so
369/// accept an axis position one beyond the current last: `dim_insert`, `stack`,
370/// `into_unpack_array`.
371#[macro_export]
372macro_rules! rstsr_check_axis_insert {
373    ($axis:expr, $ndim:expr) => {{
374        let axis: isize = $axis;
375        let ndim: usize = $ndim;
376        // insert positions fold as `ndim + axis + 1` so that `-1` inserts at the end.
377        let norm = if axis < 0 { (ndim as isize) + axis + 1 } else { axis };
378        if norm >= 0 && (norm as usize) <= ndim {
379            core::result::Result::Ok(norm as usize)
380        } else {
381            core::result::Result::Err($crate::error::Error {
382                inner: $crate::error::RSTSRError::AxisError { axis, ndim },
383                backtrace: $crate::error::rstsr_backtrace(),
384            })
385        }
386    }};
387}
388
389/// Construct an `AxisError` unconditionally (without validating). Use when a
390/// site has already determined the axis is invalid, or when raising for a
391/// related reason where the `(axis, ndim)` pair is still the right payload.
392#[macro_export]
393macro_rules! rstsr_axis_error {
394    ($axis:expr, $ndim:expr) => {{
395        $crate::error::Error {
396            inner: $crate::error::RSTSRError::AxisError { axis: $axis, ndim: $ndim },
397            backtrace: $crate::error::rstsr_backtrace(),
398        }
399    }};
400}
401
402#[macro_export]
403macro_rules! rstsr_pattern {
404    ($value:expr, $pattern:expr, $errtype:ident) => {
405        if ($pattern).contains(&($value)) {
406            Ok(())
407        } else {
408            use $crate::prelude_dev::*;
409            let mut s = String::new();
410            write!(s, concat!(file!(), ":", line!(), ": ")).unwrap();
411            write!(s, concat!("Error::", stringify!($errtype))).unwrap();
412            write!(
413                s,
414                " : {:?} = {:?} not match to pattern {:} = {:?}",
415                stringify!($value),
416                $value,
417                stringify!($pattern),
418                $pattern
419            )
420            .unwrap();
421            Err(Error{ inner: RSTSRError::$errtype(s), backtrace: rstsr_backtrace() })
422        }
423    };
424    ($value:expr, $pattern:expr, $errtype:ident, $($arg:tt)*) => {
425        if ($pattern).contains(&($value)) {
426            Ok(())
427        } else {
428            use $crate::prelude_dev::*;
429            let mut s = String::new();
430            write!(s, concat!(file!(), ":", line!(), ": ")).unwrap();
431            write!(s, concat!("Error::", stringify!($errtype))).unwrap();
432            write!(s, " : ").unwrap();
433            write!(s, $($arg)*).unwrap();
434            write!(
435                s,
436                " : {:?} = {:?} not match to pattern {:} = {:?}",
437                stringify!($value),
438                $value,
439                stringify!($pattern),
440                $pattern
441            )
442            .unwrap();
443            Err(Error{ inner: RSTSRError::$errtype(s), backtrace: rstsr_backtrace() })
444        }
445    };
446}