wolfram_library_link/
errors.rs1use std::os::raw::c_int;
15
16use crate::expr::Expr;
17use wolfram_serialize::Failure;
18
19const OFFSET: c_int = 1000;
22#[doc(hidden)]
23pub const FAILED_TO_INIT: c_int = OFFSET + 1;
24#[doc(hidden)]
25pub const FAILED_WITH_PANIC: c_int = OFFSET + 2;
26
27#[derive(Debug, Clone, Failure)]
33pub enum LibraryError {
34 RustPanic {
39 message: String,
41 source_location: String,
43 backtrace: Expr,
45 },
46 Loader {
49 message: String,
51 expected: String,
53 got: Expr,
55 },
56 #[cfg(feature = "wstp")]
58 WstpError {
59 code: i32,
61 message: String,
63 },
64 #[cfg(feature = "wstp")]
66 WstpErrorMessage {
67 message: String,
69 },
70}
71
72#[cfg(feature = "wstp")]
73impl From<wstp::Error> for LibraryError {
74 fn from(e: wstp::Error) -> Self {
75 match e.code() {
76 Some(code) => LibraryError::WstpError {
77 code,
78 message: e.to_string(),
79 },
80 None => LibraryError::WstpErrorMessage {
81 message: e.to_string(),
82 },
83 }
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use crate::expr::{expr, ExprKind};
91
92 fn failure_tag(e: &Expr) -> &str {
93 let ExprKind::Normal(n) = e.kind() else {
94 panic!("expected Normal, got {:?}", e);
95 };
96 let ExprKind::String(s) = n.elements()[0].kind() else {
97 panic!("expected String tag, got {:?}", n.elements()[0]);
98 };
99 s.as_str()
100 }
101
102 #[test]
103 fn rust_panic_is_failure_with_backtrace_expr() {
104 let backtrace = expr!(System::Missing["NotEnabled"]);
105 let err = LibraryError::RustPanic {
106 message: "boom".into(),
107 source_location: "src/x.rs:1".into(),
108 backtrace: backtrace.clone(),
109 };
110 let e = Expr::from(&err);
111 let ExprKind::Normal(normal) = e.kind() else {
112 panic!("expected Failure[...], got {:?}", e);
113 };
114 let ExprKind::Symbol(head) = normal.head().kind() else {
115 panic!("expected Symbol head");
116 };
117 assert_eq!(head.as_str(), "System`Failure");
118 let ExprKind::String(tag) = normal.elements()[0].kind() else {
119 panic!("expected String tag");
120 };
121 assert_eq!(tag.as_str(), "RustPanic");
122 let ExprKind::Association(assoc) = normal.elements()[1].kind() else {
123 panic!("expected Association");
124 };
125 let find = |k: &str| {
126 assoc
127 .iter()
128 .find(|e| e.key == Expr::from(k))
129 .map(|e| e.value.clone())
130 };
131 assert_eq!(find("Message"), Some(Expr::from("boom")));
133 assert_eq!(find("SourceLocation"), Some(Expr::from("src/x.rs:1")));
134 assert_eq!(find("Backtrace"), Some(backtrace));
136 }
137
138 #[test]
139 fn every_variant_renders_a_failure() {
140 let backtrace = Expr::string("bt");
141 let variants = [
142 LibraryError::RustPanic {
143 message: "m".into(),
144 source_location: "l".into(),
145 backtrace,
146 },
147 LibraryError::Loader {
148 message: "m".into(),
149 expected: "e".into(),
150 got: Expr::from(1i64),
151 },
152 ];
153 for v in &variants {
154 let e = Expr::from(v);
156 let ExprKind::Normal(normal) = e.kind() else {
157 panic!("expected Failure[...], got {:?}", e);
158 };
159 let ExprKind::Symbol(head) = normal.head().kind() else {
160 panic!("expected Symbol head");
161 };
162 assert_eq!(head.as_str(), "System`Failure");
163 assert!(!failure_tag(&e).is_empty());
164 assert_eq!(normal.elements().len(), 2, "must carry an association");
165 }
166 }
167}