use std::ffi::CStr;
use std::fmt;
#[derive(Debug, Clone)]
pub struct Error {
pub code: i32,
pub message: String,
}
impl Error {
pub(crate) fn capture(code: i32) -> Self {
let message = unsafe {
let ptr = empyrean_sys::empyrean_last_error();
if ptr.is_null() {
String::new()
} else {
CStr::from_ptr(ptr).to_string_lossy().into_owned()
}
};
Error { code, message }
}
pub(crate) fn from_null_ptr() -> Self {
Self::capture(-1)
}
pub(crate) fn invalid_input(msg: impl Into<String>) -> Self {
Error {
code: -1,
message: msg.into(),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.message.is_empty() {
write!(f, "empyrean error (code {})", self.code)
} else {
write!(f, "{} (code {})", self.message, self.code)
}
}
}
impl std::error::Error for Error {}
pub(crate) fn dedupe_io_prefix(msg: &str) -> String {
let mut s = msg.to_string();
while s.starts_with("I/O error: I/O error: ") {
s = s.replacen("I/O error: ", "", 1);
}
s
}
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(test)]
mod tests {
use super::dedupe_io_prefix;
#[test]
fn collapses_doubled_io_prefix() {
assert_eq!(
dedupe_io_prefix("I/O error: I/O error: No such file or directory (os error 2)"),
"I/O error: No such file or directory (os error 2)"
);
assert_eq!(dedupe_io_prefix("I/O error: nope"), "I/O error: nope");
assert_eq!(dedupe_io_prefix("convergence failed"), "convergence failed");
assert_eq!(
dedupe_io_prefix("I/O error: I/O error: I/O error: boom"),
"I/O error: boom"
);
}
}