use crate::stringutils::status_to_string;
use std::ffi::{IntoStringError, NulError};
use std::io;
use std::ops::Range;
use std::str::Utf8Error;
use std::string::FromUtf8Error;
use std::sync;
#[derive(Debug)]
pub enum Error {
Fits(FitsError),
Index(IndexError),
Message(String),
#[allow(clippy::incompatible_msrv)]
Null(NulError),
Utf8(Utf8Error),
Io(io::Error),
#[allow(clippy::incompatible_msrv)]
IntoString(IntoStringError),
ExistingFile(String),
UnlockError,
NullPointer,
}
#[derive(Debug, PartialEq, Eq)]
pub struct IndexError {
pub message: String,
pub given: Range<usize>,
}
pub type Result<T> = ::std::result::Result<T, Error>;
impl ::std::convert::From<FitsError> for Error {
fn from(error: FitsError) -> Self {
Error::Fits(error)
}
}
impl ::std::convert::From<IndexError> for Error {
fn from(error: IndexError) -> Self {
Error::Index(error)
}
}
impl<'a> ::std::convert::From<&'a str> for Error {
fn from(error: &'a str) -> Self {
Error::Message(error.to_string())
}
}
#[allow(clippy::incompatible_msrv)]
impl ::std::convert::From<NulError> for Error {
fn from(error: NulError) -> Self {
Error::Null(error)
}
}
impl ::std::convert::From<FromUtf8Error> for Error {
fn from(error: FromUtf8Error) -> Self {
Error::Utf8(error.utf8_error())
}
}
impl ::std::convert::From<Utf8Error> for Error {
fn from(error: Utf8Error) -> Self {
Error::Utf8(error)
}
}
impl ::std::convert::From<Box<dyn ::std::error::Error>> for Error {
fn from(error: Box<dyn ::std::error::Error>) -> Self {
let message = match error.source() {
Some(msg) => format!("Error: {} caused by {}", error, msg),
None => format!("Error: {}", error),
};
Error::Message(message)
}
}
impl ::std::convert::From<io::Error> for Error {
fn from(e: io::Error) -> Self {
Error::Io(e)
}
}
#[allow(clippy::incompatible_msrv)]
impl ::std::convert::From<IntoStringError> for Error {
fn from(e: IntoStringError) -> Self {
Error::IntoString(e)
}
}
use crate::fitsfile::FitsFile;
type PoisonError<'a> = sync::PoisonError<sync::MutexGuard<'a, FitsFile>>;
impl ::std::convert::From<PoisonError<'_>> for Error {
fn from(_e: PoisonError) -> Self {
Error::UnlockError
}
}
impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
match *self {
Error::Fits(ref e) => write!(f, "Fits error: {:?}", e),
Error::Message(ref s) => write!(f, "Error: {}", s),
Error::Null(ref e) => e.fmt(f),
Error::Utf8(ref e) => e.fmt(f),
Error::Index(ref e) => write!(f, "Error: {:?}", e),
Error::Io(ref e) => e.fmt(f),
Error::IntoString(ref e) => e.fmt(f),
Error::ExistingFile(ref filename) => write!(f, "File {} already exists", filename),
Error::UnlockError => write!(f, "Invalid concurrent access to fits file"),
Error::NullPointer => write!(f, "Null pointer specified"),
}
}
}
impl ::std::error::Error for Error {
fn description(&self) -> &str {
"fitsio error"
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct FitsError {
pub status: i32,
pub message: String,
}
pub fn check_status(status: i32) -> Result<()> {
match status {
0 => Ok(()),
_ => Err(Error::Fits(FitsError {
status,
message: status_to_string(status)?.expect("guaranteed to be Some"),
})),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_check_status_ok() {
assert!(check_status(0).is_ok());
}
#[test]
fn test_check_status_ok_with_value() {
assert_eq!(check_status(0).map(|_| 10i32).unwrap(), 10i32);
}
#[test]
fn test_check_status_with_err() {
assert!(check_status(105).map(|_| 10i32).is_err());
}
#[test]
fn test_message() {
assert_eq!(
format!("{}", Error::Message("bad".to_string())),
"Error: bad"
);
}
}