use std::ffi::CStr;
use std::fmt;
#[derive(Debug, Clone)]
pub struct Error {
pub code: i32,
pub message: String,
pub missing_data_files: Vec<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,
missing_data_files: Vec::new(),
}
}
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(),
missing_data_files: Vec::new(),
}
}
pub fn missing_data_files(&self) -> &[String] {
&self.missing_data_files
}
}
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) const DOWNLOAD_FAILED_PREFIX: &str = "Data download failed: ";
pub(crate) const IO_ERROR_PREFIX: &str = "I/O error: ";
pub(crate) fn dedupe_io_prefix(msg: &str) -> String {
let mut kept = String::new();
let mut rest = msg;
while let Some((prefix, tail)) = [DOWNLOAD_FAILED_PREFIX, IO_ERROR_PREFIX]
.into_iter()
.find_map(|p| rest.strip_prefix(p).map(|tail| (p, tail)))
{
if !kept.ends_with(prefix) {
kept.push_str(prefix);
}
rest = tail;
}
kept.push_str(rest);
kept
}
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"
);
}
#[test]
fn collapses_behind_the_download_prefix() {
assert_eq!(
dedupe_io_prefix("Data download failed: I/O error: I/O error: boom"),
"Data download failed: I/O error: boom"
);
assert_eq!(
dedupe_io_prefix("Data download failed: Data download failed: GET https://x/y: 404"),
"Data download failed: GET https://x/y: 404"
);
assert_eq!(
dedupe_io_prefix(
"Data download failed: GET https://naif.jpl.nasa.gov/x.bpc: http status: 404"
),
"Data download failed: GET https://naif.jpl.nasa.gov/x.bpc: http status: 404"
);
assert_eq!(
dedupe_io_prefix("I/O error: Data download failed: nope"),
"I/O error: Data download failed: nope"
);
}
}