1use thiserror::Error;
2use pyo3::types::PyTracebackMethods;
3use std::ffi::NulError;
4
5#[derive(Error, Debug)]
7pub enum Error {
8 #[error("Python error: {0}")]
10 Py(#[from] pyo3::PyErr),
11
12 #[error("Runtime error: {0}")]
14 Interp(String),
15
16 #[error("Type conversion error: {0}")]
18 Conversion(String),
19
20 #[error("Failed to import module '{module}': {reason}")]
22 Import {
23 module: String,
24 reason: String,
25 },
26
27 #[error("Failed to call function '{function}': {reason}")]
29 Call {
30 function: String,
31 reason: String,
32 },
33
34 #[error("Python runtime not initialized. Call rustpy_ml::init() first.")]
36 NotInitialized,
37
38 #[error("String contains null byte: {0}")]
40 NulByte(#[from] NulError),
41
42 #[error(transparent)]
44 Anyhow(#[from] anyhow::Error),
45}
46
47impl Error {
48 pub fn interp(msg: impl Into<String>) -> Self {
50 Error::Interp(msg.into())
51 }
52
53 pub fn conversion(msg: impl Into<String>) -> Self {
55 Error::Conversion(msg.into())
56 }
57
58 pub fn import(module: impl Into<String>, reason: impl Into<String>) -> Self {
60 Error::Import {
61 module: module.into(),
62 reason: reason.into(),
63 }
64 }
65
66 pub fn call(function: impl Into<String>, reason: impl Into<String>) -> Self {
68 Error::Call {
69 function: function.into(),
70 reason: reason.into(),
71 }
72 }
73
74 pub fn traceback(&self) -> Option<String> {
76 match self {
77 Error::Py(err) => {
78 pyo3::Python::with_gil(|py| {
79 err.traceback(py).and_then(|tb| {
80 tb.format().ok()
81 })
82 })
83 }
84 _ => None,
85 }
86 }
87
88 pub fn print_detailed(&self) {
90 eprintln!("Error: {}", self);
91 if let Some(tb) = self.traceback() {
92 eprintln!("Traceback:\n{}", tb);
93 }
94 }
95}
96
97pub type Result<T> = std::result::Result<T, Error>;
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[test]
105 fn test_error_creation() {
106 let err = Error::interp("test error");
107 assert!(matches!(err, Error::Interp(_)));
108
109 let err = Error::import("numpy", "not found");
110 assert!(matches!(err, Error::Import { .. }));
111
112 let err = Error::call("test_func", "invalid args");
113 assert!(matches!(err, Error::Call { .. }));
114 }
115}