use std::os::raw::c_int;
use crate::expr::{Expr, Failure};
const OFFSET: c_int = 1000;
pub const FAILED_TO_INIT: c_int = OFFSET + 1;
pub const FAILED_WITH_PANIC: c_int = OFFSET + 2;
#[derive(Debug, Clone, Failure)]
pub enum LibraryError {
RustPanic {
message: String,
source_location: String,
backtrace: Expr,
},
Loader {
message: String,
expected: String,
got: Expr,
},
#[cfg(feature = "wstp")]
WstpError {
code: i32,
message: String,
},
#[cfg(feature = "wstp")]
WstpErrorMessage {
message: String,
},
}
#[cfg(feature = "wstp")]
impl From<wstp::Error> for LibraryError {
fn from(e: wstp::Error) -> Self {
match e.code() {
Some(code) => LibraryError::WstpError {
code,
message: e.to_string(),
},
None => LibraryError::WstpErrorMessage {
message: e.to_string(),
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::expr::{expr, ExprKind};
fn failure_tag(e: &Expr) -> &str {
let ExprKind::Normal(n) = e.kind() else {
panic!("expected Normal, got {:?}", e);
};
let ExprKind::String(s) = n.elements()[0].kind() else {
panic!("expected String tag, got {:?}", n.elements()[0]);
};
s.as_str()
}
#[test]
fn rust_panic_is_failure_with_backtrace_expr() {
let backtrace = expr!(System::Missing["NotEnabled"]);
let err = LibraryError::RustPanic {
message: "boom".into(),
source_location: "src/x.rs:1".into(),
backtrace: backtrace.clone(),
};
let e = Expr::from(&err);
let ExprKind::Normal(normal) = e.kind() else {
panic!("expected Failure[...], got {:?}", e);
};
let ExprKind::Symbol(head) = normal.head().kind() else {
panic!("expected Symbol head");
};
assert_eq!(head.as_str(), "System`Failure");
let ExprKind::String(tag) = normal.elements()[0].kind() else {
panic!("expected String tag");
};
assert_eq!(tag.as_str(), "RustPanic");
let ExprKind::Association(assoc) = normal.elements()[1].kind() else {
panic!("expected Association");
};
let find = |k: &str| {
assoc
.iter()
.find(|e| e.key == Expr::from(k))
.map(|e| e.value.clone())
};
assert_eq!(find("Message"), Some(Expr::from("boom")));
assert_eq!(find("SourceLocation"), Some(Expr::from("src/x.rs:1")));
assert_eq!(find("Backtrace"), Some(backtrace));
}
#[test]
fn every_variant_renders_a_failure() {
let backtrace = Expr::string("bt");
let variants = [
LibraryError::RustPanic {
message: "m".into(),
source_location: "l".into(),
backtrace,
},
LibraryError::Loader {
message: "m".into(),
expected: "e".into(),
got: Expr::from(1i64),
},
];
for v in &variants {
let e = Expr::from(v);
let ExprKind::Normal(normal) = e.kind() else {
panic!("expected Failure[...], got {:?}", e);
};
let ExprKind::Symbol(head) = normal.head().kind() else {
panic!("expected Symbol head");
};
assert_eq!(head.as_str(), "System`Failure");
assert!(!failure_tag(&e).is_empty());
assert_eq!(normal.elements().len(), 2, "must carry an association");
}
}
}