use crate::common::{GET, SET};
use crate::string::{SpiceStr, SpiceString};
use crate::with_spice_lock_or_panic;
use cspice_sys::{
erract_c, errdev_c, failed_c, getmsg_c, qcktrc_c, reset_c, SpiceInt, SPICE_ERROR_LMSGLN,
SPICE_ERROR_SMSGLN, SPICE_ERROR_TRCLEN, SPICE_ERROR_XMSGLN,
};
use serde::{Deserialize, Serialize};
use thiserror::Error;
const FILEN: SpiceInt = 255;
#[derive(Debug, Clone, Error)]
#[error("{short_message}\n\n{explanation}\n\n{long_message}\n\nTraceback:\n{traceback}")]
pub struct Error {
pub short_message: String,
pub explanation: String,
pub long_message: String,
pub traceback: String,
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "UPPERCASE")]
pub enum ErrorAction {
Abort,
Ignore,
Report,
Return,
Default,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ErrorDevice {
Screen,
Null,
Filename(String),
}
#[inline]
pub fn get_last_error() -> Result<(), Error> {
with_spice_lock_or_panic(|| {
unsafe {
if failed_c() == 0 {
return Ok(());
}
let option = SpiceString::from("SHORT");
let mut short_message = [0; SPICE_ERROR_SMSGLN as usize];
getmsg_c(
option.as_mut_ptr(),
short_message.len() as SpiceInt,
short_message.as_mut_ptr(),
);
let option = SpiceString::from("EXPLAIN");
let mut explanation = [0; SPICE_ERROR_XMSGLN as usize];
getmsg_c(
option.as_mut_ptr(),
explanation.len() as SpiceInt,
explanation.as_mut_ptr(),
);
let option = SpiceString::from("LONG");
let mut long_message = [0; SPICE_ERROR_LMSGLN as usize];
getmsg_c(
option.as_mut_ptr(),
long_message.len() as SpiceInt,
long_message.as_mut_ptr(),
);
let mut traceback = [0; SPICE_ERROR_TRCLEN as usize];
qcktrc_c(traceback.len() as SpiceInt, traceback.as_mut_ptr());
reset_c();
Err(Error {
short_message: SpiceStr::from_buffer(&short_message).to_string(),
explanation: SpiceStr::from_buffer(&explanation).to_string(),
long_message: SpiceStr::from_buffer(&long_message).to_string(),
traceback: SpiceStr::from_buffer(&traceback).to_string(),
})
}
})
}
pub fn set_error_action(action: ErrorAction) -> Result<(), Error> {
let action = SpiceString::from(serde_plain::to_string(&action).unwrap());
with_spice_lock_or_panic(|| {
unsafe { erract_c(SET.as_mut_ptr(), 0, action.as_mut_ptr()) };
get_last_error()
})
}
pub fn get_error_action() -> Result<ErrorAction, Error> {
let mut buffer = [0; 20];
with_spice_lock_or_panic(|| {
unsafe {
erract_c(
GET.as_mut_ptr(),
buffer.len() as SpiceInt,
buffer.as_mut_ptr(),
)
};
get_last_error()
})?;
let action = SpiceStr::from_buffer(&buffer);
Ok(serde_plain::from_str(&action.as_str()).unwrap())
}
pub fn set_error_output_device(device: ErrorDevice) -> Result<(), Error> {
let device = match device {
ErrorDevice::Screen => SpiceString::from("SCREEN"),
ErrorDevice::Null => SpiceString::from("NULL"),
ErrorDevice::Filename(filename) => SpiceString::from(filename),
};
with_spice_lock_or_panic(|| {
unsafe { errdev_c(SET.as_mut_ptr(), 0, device.as_mut_ptr()) };
get_last_error()
})
}
pub fn get_error_output_device() -> Result<ErrorDevice, Error> {
let mut buffer = [0; FILEN as usize];
with_spice_lock_or_panic(|| {
unsafe {
errdev_c(
GET.as_mut_ptr(),
buffer.len() as SpiceInt,
buffer.as_mut_ptr(),
);
};
get_last_error()
})?;
let action = SpiceStr::from_buffer(&buffer);
Ok(match action.as_str() {
s if s == "SCREEN" => ErrorDevice::Screen,
s if s == "NULL" => ErrorDevice::Null,
s => ErrorDevice::Filename(s.into_owned()),
})
}
pub(crate) fn set_error_defaults() {
set_error_action(ErrorAction::Return).unwrap();
set_error_output_device(ErrorDevice::Null).unwrap();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_set_error_action() {
set_error_action(ErrorAction::Default).unwrap();
assert_eq!(get_error_action().unwrap(), ErrorAction::Default);
set_error_action(ErrorAction::Ignore).unwrap();
assert_eq!(get_error_action().unwrap(), ErrorAction::Ignore);
set_error_action(ErrorAction::Abort).unwrap();
assert_eq!(get_error_action().unwrap(), ErrorAction::Abort);
set_error_defaults();
}
#[test]
fn test_get_set_error_output_device() {
set_error_output_device(ErrorDevice::Null).unwrap();
assert_eq!(get_error_output_device().unwrap(), ErrorDevice::Null);
set_error_output_device(ErrorDevice::Screen).unwrap();
assert_eq!(get_error_output_device().unwrap(), ErrorDevice::Screen);
let filename = ErrorDevice::Filename(String::from("errors.txt"));
set_error_output_device(filename.clone()).unwrap();
assert_eq!(get_error_output_device().unwrap(), filename);
set_error_defaults();
}
}