pub(crate) mod error;
pub use error::FitsError;
use fitsio::{hdu::*, FitsFile};
use log::error;
use log::trace;
use std::ffi::*;
use std::ptr;
#[cfg(test)]
mod test;
#[macro_export]
macro_rules! fits_open {
($fptr:expr) => {
$crate::_open_fits($fptr, file!(), line!())
};
}
#[macro_export]
macro_rules! fits_open_hdu {
($fptr:expr, $hdu_num:expr) => {
$crate::_open_hdu($fptr, $hdu_num, file!(), line!())
};
}
#[macro_export]
macro_rules! fits_open_hdu_by_name {
($fptr:expr, $hdu_name:expr) => {
$crate::_open_hdu_by_name($fptr, $hdu_name, file!(), line!())
};
}
#[macro_export]
macro_rules! get_required_fits_key {
($fptr:expr, $hdu:expr, $keyword:expr) => {
_get_required_fits_key($fptr, $hdu, $keyword, file!(), line!())
};
}
#[macro_export]
macro_rules! get_fits_col {
($fptr:expr, $hdu:expr, $keyword:expr) => {
_get_fits_col($fptr, $hdu, $keyword, file!(), line!())
};
}
#[macro_export]
macro_rules! get_hdu_image_size {
($fptr:expr, $hdu:expr) => {
_get_hdu_image_size($fptr, $hdu, file!(), line!())
};
}
#[macro_export]
macro_rules! get_optional_fits_key_long_string {
($fptr:expr, $hdu:expr, $keyword:expr) => {
_get_optional_fits_key_long_string($fptr, $hdu, $keyword, file!(), line!())
};
}
#[macro_export]
macro_rules! get_required_fits_key_long_string {
($fptr:expr, $hdu:expr, $keyword:expr) => {
_get_required_fits_key_long_string($fptr, $hdu, $keyword, file!(), line!())
};
}
#[macro_export]
macro_rules! get_fits_image {
($fptr:expr, $hdu:expr) => {
_get_fits_image($fptr, $hdu, file!(), line!())
};
}
#[macro_export]
macro_rules! get_fits_float_image_into_buffer {
($fptr:expr, $hdu:expr, $buffer:expr) => {
_get_fits_float_img_into_buf($fptr, $hdu, $buffer, file!(), line!())
};
}
#[doc(hidden)]
pub fn _open_fits<T: AsRef<std::path::Path>>(
file: T,
source_file: &'static str,
source_line: u32,
) -> Result<FitsFile, FitsError> {
match FitsFile::open(&file) {
Ok(f) => {
trace!("_open_fits() filename: '{}'", file.as_ref().display());
Ok(f)
}
Err(e) => Err(FitsError::Open {
fits_error: e,
fits_filename: file.as_ref().to_path_buf(),
source_file,
source_line,
}),
}
}
#[doc(hidden)]
pub fn _open_hdu(
fits_fptr: &mut FitsFile,
hdu_num: usize,
source_file: &'static str,
source_line: u32,
) -> Result<FitsHdu, FitsError> {
match fits_fptr.hdu(hdu_num) {
Ok(f) => {
trace!(
"_open_hdu() filename: '{}' hdu: {}",
fits_fptr.file_path().display(),
hdu_num
);
Ok(f)
}
Err(e) => Err(FitsError::Fitsio {
fits_error: e,
fits_filename: fits_fptr.file_path().to_path_buf(),
hdu: format!("{}", hdu_num + 1),
source_file,
source_line,
}),
}
}
#[doc(hidden)]
pub fn _open_hdu_by_name(
fits_fptr: &mut FitsFile,
hdu_name: &'static str,
source_file: &'static str,
source_line: u32,
) -> Result<FitsHdu, FitsError> {
match fits_fptr.hdu(hdu_name) {
Ok(f) => {
trace!(
"_open_hdu_by_name() filename: '{}' hdu: {}",
fits_fptr.file_path().display(),
hdu_name
);
Ok(f)
}
Err(e) => Err(FitsError::Fitsio {
fits_error: e,
fits_filename: fits_fptr.file_path().to_path_buf(),
hdu: hdu_name.to_string(),
source_file,
source_line,
}),
}
}
#[doc(hidden)]
pub fn _get_optional_fits_key<T: std::str::FromStr>(
fits_fptr: &mut FitsFile,
hdu: &FitsHdu,
keyword: &str,
source_file: &'static str,
source_line: u32,
) -> Result<Option<T>, FitsError> {
let unparsed_value: String = match hdu.read_key(fits_fptr, keyword) {
Ok(key_value) => key_value,
Err(e) => match &e {
fitsio::errors::Error::Fits(fe) => match fe.status {
202 | 204 => return Ok(None),
_ => {
return Err(FitsError::Fitsio {
fits_error: e,
fits_filename: fits_fptr.file_path().to_path_buf(),
hdu: format!("{}", hdu.number + 1),
source_file,
source_line,
})
}
},
_ => {
return Err(FitsError::Fitsio {
fits_error: e,
fits_filename: fits_fptr.file_path().to_path_buf(),
hdu: format!("{}", hdu.number + 1),
source_file,
source_line,
})
}
},
};
trace!(
"_get_optional_fits_key() filename: '{}' hdu: {} keyword: '{}' value: '{}'",
fits_fptr.file_path().display(),
hdu.number,
String::from(keyword),
unparsed_value
);
match unparsed_value.parse() {
Ok(parsed_value) => Ok(Some(parsed_value)),
Err(_) => Err(FitsError::Parse {
key: keyword.to_string(),
fits_filename: fits_fptr.file_path().to_path_buf(),
hdu_num: hdu.number + 1,
source_file,
source_line,
}),
}
}
#[doc(hidden)]
pub fn _get_required_fits_key<T: std::str::FromStr>(
fits_fptr: &mut FitsFile,
hdu: &FitsHdu,
keyword: &str,
source_file: &'static str,
source_line: u32,
) -> Result<T, FitsError> {
match _get_optional_fits_key(fits_fptr, hdu, keyword, source_file, source_line) {
Ok(Some(value)) => Ok(value),
Ok(None) => Err(FitsError::MissingKey {
key: keyword.to_string(),
fits_filename: fits_fptr.file_path().to_path_buf(),
hdu_num: hdu.number + 1,
source_file,
source_line,
}),
Err(error) => Err(error),
}
}
#[doc(hidden)]
pub fn _get_fits_col<T: fitsio::tables::ReadsCol>(
fits_fptr: &mut FitsFile,
hdu: &FitsHdu,
keyword: &str,
source_file: &'static str,
source_line: u32,
) -> Result<Vec<T>, FitsError> {
match hdu.read_col(fits_fptr, keyword) {
Ok(c) => {
trace!(
"_get_fits_col() filename: '{}' hdu: {} keyword: '{}' values: {}",
fits_fptr.file_path().display(),
hdu.number,
keyword,
c.len()
);
Ok(c)
}
Err(fits_error) => Err(FitsError::Fitsio {
fits_error,
fits_filename: fits_fptr.file_path().to_path_buf(),
hdu: format!("{}", hdu.number + 1),
source_file,
source_line,
}),
}
}
#[doc(hidden)]
pub fn _get_optional_fits_key_long_string(
fits_fptr: &mut FitsFile,
hdu: &FitsHdu,
keyword: &str,
source_file: &'static str,
source_line: u32,
) -> Result<Option<String>, FitsError> {
let keyword_ffi = CString::new(keyword)
.expect("_get_optional_fits_key_long_string: CString::new() failed for keyword");
let long_string = unsafe {
let mut status = 0;
let mut long_string_ptr = ptr::null_mut();
fitsio_sys::ffgkls(
fits_fptr.as_raw(),
keyword_ffi.as_ptr(),
&mut long_string_ptr,
ptr::null_mut(),
&mut status,
);
match status {
0 => {
let long_string = CString::from_raw(long_string_ptr)
.into_string()
.expect("_get_optional_fits_key_long_string: converting string_ptr failed");
Some(long_string)
}
202 | 204 => None,
_ => {
return Err(FitsError::LongString {
key: keyword.to_string(),
fits_filename: fits_fptr.file_path().to_path_buf(),
hdu_num: hdu.number + 1,
source_file,
source_line,
})
}
}
};
trace!(
"_get_optional_fits_key_long_string() filename: {} keyword: '{}' value: '{:?}'",
fits_fptr.file_path().display(),
keyword,
long_string
);
Ok(long_string)
}
#[doc(hidden)]
pub fn _get_required_fits_key_long_string(
fits_fptr: &mut FitsFile,
hdu: &FitsHdu,
keyword: &str,
source_file: &'static str,
source_line: u32,
) -> Result<String, FitsError> {
match _get_optional_fits_key_long_string(fits_fptr, hdu, keyword, source_file, source_line) {
Ok(Some(value)) => Ok(value),
Ok(None) => Err(FitsError::MissingKey {
key: keyword.to_string(),
fits_filename: fits_fptr.file_path().to_path_buf(),
hdu_num: hdu.number + 1,
source_file,
source_line,
}),
Err(error) => Err(error),
}
}
#[doc(hidden)]
pub fn _get_hdu_image_size(
fits_fptr: &mut FitsFile,
hdu: &FitsHdu,
source_file: &'static str,
source_line: u32,
) -> Result<Vec<usize>, FitsError> {
match &hdu.info {
HduInfo::ImageInfo { shape, .. } => {
trace!(
"_get_hdu_image_size() filename: '{}' hdu: {} shape: {:?}",
fits_fptr.file_path().display(),
hdu.number,
shape
);
Ok(shape.clone())
}
_ => Err(FitsError::NotImage {
fits_filename: fits_fptr.file_path().to_path_buf(),
hdu_num: hdu.number + 1,
source_file,
source_line,
}),
}
}
#[doc(hidden)]
pub fn _get_fits_image<T: fitsio::images::ReadImage>(
fits_fptr: &mut FitsFile,
hdu: &FitsHdu,
source_file: &'static str,
source_line: u32,
) -> Result<T, FitsError> {
match &hdu.info {
HduInfo::ImageInfo { .. } => match hdu.read_image(fits_fptr) {
Ok(img) => {
trace!(
"_get_fits_image() filename: '{}' hdu: {}",
fits_fptr.file_path().display(),
hdu.number
);
Ok(img)
}
Err(e) => Err(FitsError::Fitsio {
fits_error: e,
fits_filename: fits_fptr.file_path().to_path_buf(),
hdu: format!("{}", hdu.number + 1),
source_file,
source_line,
}),
},
_ => Err(FitsError::NotImage {
fits_filename: fits_fptr.file_path().to_path_buf(),
hdu_num: hdu.number + 1,
source_file,
source_line,
}),
}
}
#[doc(hidden)]
pub fn _get_fits_float_img_into_buf(
fits_fptr: &mut FitsFile,
hdu: &FitsHdu,
buffer: &mut [f32],
source_file: &'static str,
source_line: u32,
) -> Result<(), FitsError> {
unsafe {
let buffer_len = buffer.len() as i64;
let buffer_ptr = buffer.as_mut_ptr();
let mut status = 0;
fitsio_sys::ffgpv(
fits_fptr.as_raw(),
fitsio_sys::TFLOAT as _,
1,
buffer_len,
ptr::null_mut(),
buffer_ptr as *mut _,
ptr::null_mut(),
&mut status,
);
match fitsio::errors::check_status(status) {
Ok(_) => {}
Err(e) => {
return Err(FitsError::Fitsio {
fits_error: e,
fits_filename: fits_fptr.file_path().to_path_buf(),
hdu: format!("{}", hdu.number + 1),
source_file,
source_line,
});
}
}
}
trace!(
"_get_fits_float_img_into_buf() filename: '{}' hdu: {}",
fits_fptr.file_path().display(),
hdu.number
);
Ok(())
}
pub fn read_optional_cell_value<T: fitsio::tables::ReadsCol>(
fits_fptr: &mut FitsFile,
fits_tile_table_hdu: &fitsio::hdu::FitsHdu,
col_name: &str,
row: usize,
) -> Result<Option<T>, FitsError> {
match fits_tile_table_hdu.read_cell_value(fits_fptr, col_name, row) {
Ok(c) => {
trace!(
"read_cell_value() filename: '{}' hdu: {} col_name: '{}' row '{}'",
fits_fptr.file_path().display(),
fits_tile_table_hdu.number,
col_name,
row
);
Ok(Some(c))
}
Err(e) => match e {
fitsio::errors::Error::Null(_) => Ok(None),
_ => Err(FitsError::ReadCell {
fits_filename: fits_fptr.file_path().to_path_buf(),
hdu_num: fits_tile_table_hdu.number + 1,
row_num: row,
col_name: col_name.to_string(),
}),
},
}
}
pub fn read_optional_cell_string_value(
fits_fptr: &mut FitsFile,
fits_tile_table_hdu: &fitsio::hdu::FitsHdu,
col_name: &str,
row: usize,
) -> Result<Option<String>, FitsError> {
match fits_tile_table_hdu.read_cell_value::<String>(fits_fptr, col_name, row) {
Ok(c) => {
trace!(
"read_cell_value() filename: '{}' hdu: {} col_name: '{}' row '{}'",
fits_fptr.file_path().display(),
fits_tile_table_hdu.number,
col_name,
row
);
if c.is_empty() {
Ok(None)
} else {
Ok(Some(c))
}
}
Err(e) => match e {
fitsio::errors::Error::Null(_) => Ok(None),
_ => Err(FitsError::ReadCell {
fits_filename: fits_fptr.file_path().to_path_buf(),
hdu_num: fits_tile_table_hdu.number + 1,
row_num: row,
col_name: col_name.to_string(),
}),
},
}
}
pub fn read_cell_array_u32(
fits_fptr: &mut FitsFile,
fits_table_hdu: &fitsio::hdu::FitsHdu,
col_name: &str,
row: usize,
n_elem: usize,
) -> Result<Vec<u32>, FitsError> {
let col_num: i32 = get_table_col_index(fits_fptr, fits_table_hdu, col_name)?;
unsafe {
let mut status = 0;
let mut array: Vec<u32> = vec![0; n_elem];
array.shrink_to_fit();
let array_ptr = array.as_mut_ptr();
fitsio_sys::ffgcv(
fits_fptr.as_raw(),
31,
col_num,
row as i64 + 1,
1,
n_elem as i64,
std::ptr::null_mut(),
array_ptr as *mut core::ffi::c_void,
&mut 0,
&mut status,
);
match status {
0 => {
let v = std::slice::from_raw_parts(array_ptr, n_elem);
trace!(
"read_cell_array_u32() filename: '{}' hdu: {} col_name: '{}' row '{}'",
fits_fptr.file_path().display(),
fits_table_hdu.number,
col_name,
row
);
Ok(v.iter().map(|v| *v as _).collect())
}
_ => {
error!(
"ERROR {} read_cell_array_u32() filename: '{}' hdu: {} col_name: '{}' row '{}'",
status,
fits_fptr.file_path().display(),
fits_table_hdu.number,
col_name,
row
);
Err(FitsError::CellArray {
fits_filename: fits_fptr.file_path().to_path_buf(),
hdu_num: fits_table_hdu.number + 1,
row_num: row,
col_name: col_name.to_string(),
})
}
}
}
}
pub fn read_optional_cell_array_u32(
fits_fptr: &mut FitsFile,
fits_table_hdu: &fitsio::hdu::FitsHdu,
col_name: &str,
row: usize,
n_elem: usize,
) -> Result<Option<Vec<u32>>, FitsError> {
let col_num: i32 = get_table_col_index(fits_fptr, fits_table_hdu, col_name)?;
unsafe {
let mut status = 0;
let mut array: Vec<u32> = vec![0; n_elem];
array.shrink_to_fit();
let array_ptr = array.as_mut_ptr();
fitsio_sys::ffgcv(
fits_fptr.as_raw(),
31,
col_num,
row as i64 + 1,
1,
n_elem as i64,
std::ptr::null_mut(),
array_ptr as *mut core::ffi::c_void,
&mut 0,
&mut status,
);
match status {
0 => {
let v = std::slice::from_raw_parts(array_ptr, n_elem);
trace!(
"read_optional_cell_array_u32() filename: '{}' hdu: {} col_name: '{}' row '{}'",
fits_fptr.file_path().display(),
fits_table_hdu.number,
col_name,
row
);
Ok(Some(v.iter().map(|v| *v as _).collect()))
}
_ => {
error!(
"ERROR {} read_optional_cell_array_u32() filename: '{}' hdu: {} col_name: '{}' row '{}'",
status,
fits_fptr.file_path().display(),
fits_table_hdu.number,
col_name,
row
);
Err(FitsError::CellArray {
fits_filename: fits_fptr.file_path().to_path_buf(),
hdu_num: fits_table_hdu.number + 1,
row_num: row,
col_name: col_name.to_string(),
})
}
}
}
}
pub fn read_cell_array_f32(
fits_fptr: &mut FitsFile,
fits_table_hdu: &fitsio::hdu::FitsHdu,
col_name: &str,
row: usize,
n_elem: usize,
) -> Result<Vec<f32>, FitsError> {
let col_num: i32 = get_table_col_index(fits_fptr, fits_table_hdu, col_name)?;
unsafe {
let mut status = 0;
let mut array: Vec<f32> = vec![0.0; n_elem];
array.shrink_to_fit();
let array_ptr = array.as_mut_ptr();
fitsio_sys::ffgcv(
fits_fptr.as_raw(),
42,
col_num,
row as i64 + 1,
1,
n_elem as i64,
std::ptr::null_mut(),
array_ptr as *mut core::ffi::c_void,
&mut 0,
&mut status,
);
match status {
0 => {
trace!(
"read_cell_array_f32() filename: '{}' hdu: {} col_name: '{}' row '{}'",
fits_fptr.file_path().display(),
fits_table_hdu.number,
col_name,
row
);
Ok(std::slice::from_raw_parts(array_ptr, n_elem).to_vec())
}
_ => {
error!(
"ERROR {} read_cell_array_f32() filename: '{}' hdu: {} col_name: '{}' row '{}'",
status,
fits_fptr.file_path().display(),
fits_table_hdu.number,
col_name,
row
);
Err(FitsError::CellArray {
fits_filename: fits_fptr.file_path().to_path_buf(),
hdu_num: fits_table_hdu.number + 1,
row_num: row,
col_name: col_name.to_string(),
})
}
}
}
}
pub fn read_cell_array_f64(
fits_fptr: &mut FitsFile,
fits_table_hdu: &fitsio::hdu::FitsHdu,
col_name: &str,
row: usize,
n_elem: usize,
) -> Result<Vec<f64>, FitsError> {
let col_num: i32 = get_table_col_index(fits_fptr, fits_table_hdu, col_name)?;
unsafe {
let mut status: i32 = 0;
let mut array: Vec<f64> = vec![0.0; n_elem];
array.shrink_to_fit();
let array_ptr = array.as_mut_ptr();
fitsio_sys::ffgcv(
fits_fptr.as_raw(),
82,
col_num,
row as i64 + 1,
1,
n_elem as i64,
std::ptr::null_mut(),
array_ptr as *mut core::ffi::c_void,
&mut 0,
&mut status,
);
match status {
0 => {
trace!(
"read_cell_array_f64() filename: '{}' hdu: {} col_name: '{}' row '{}'",
fits_fptr.file_path().display(),
fits_table_hdu.number,
col_name,
row
);
Ok(std::slice::from_raw_parts(array_ptr, n_elem).to_vec())
}
_ => {
error!(
"ERROR {} read_cell_array_f64() filename: '{}' hdu: {} col_name: '{}' row '{}'",
status,
fits_fptr.file_path().display(),
fits_table_hdu.number,
col_name,
row
);
Err(FitsError::CellArray {
fits_filename: fits_fptr.file_path().to_path_buf(),
hdu_num: fits_table_hdu.number + 1,
row_num: row,
col_name: col_name.to_string(),
})
}
}
}
}
pub fn is_fitsio_reentrant() -> bool {
unsafe { fitsio_sys::fits_is_reentrant() == 1 }
}
pub fn read_cell_value<T: fitsio::tables::ReadsCol>(
fits_fptr: &mut FitsFile,
fits_tile_table_hdu: &fitsio::hdu::FitsHdu,
col_name: &str,
row: usize,
) -> Result<T, FitsError> {
match fits_tile_table_hdu.read_cell_value(fits_fptr, col_name, row) {
Ok(c) => {
trace!(
"read_cell_value() filename: '{}' hdu: {} col_name: '{}' row '{}'",
fits_fptr.file_path().display(),
fits_tile_table_hdu.number,
col_name,
row
);
Ok(c)
}
Err(_) => Err(FitsError::ReadCell {
fits_filename: fits_fptr.file_path().to_path_buf(),
hdu_num: fits_tile_table_hdu.number + 1,
row_num: row,
col_name: col_name.to_string(),
}),
}
}
pub fn read_cell_string(
fits_fptr: &mut FitsFile,
fits_table_hdu: &fitsio::hdu::FitsHdu,
col_name: &str,
row: usize,
) -> Result<String, FitsError> {
let col_num: i32 = get_table_col_index(fits_fptr, fits_table_hdu, col_name)?;
unsafe {
let mut status: i32 = 0;
const FLEN_VALUE: usize = 255;
let mut buffer: Vec<c_char> = vec![0u8 as c_char; FLEN_VALUE];
let mut str_ptrs: Vec<*mut c_char> = vec![buffer.as_mut_ptr() as *mut c_char];
fitsio_sys::fits_read_col_str(
fits_fptr.as_raw(),
col_num,
row as i64 + 1,
1,
1,
ptr::null_mut(),
str_ptrs.as_mut_ptr(),
ptr::null_mut(),
&mut status,
);
match status {
0 => {
trace!(
"read_cell_string() filename: '{}' hdu: {} col_name: '{}' row '{}'",
fits_fptr.file_path().display(),
fits_table_hdu.number,
col_name,
row
);
let c_str = CStr::from_ptr(str_ptrs[0]);
Ok(c_str.to_string_lossy().into_owned())
}
_ => {
error!(
"ERROR {} read_cell_string() filename: '{}' hdu: {} col_name: '{}' row '{}'",
status,
fits_fptr.file_path().display(),
fits_table_hdu.number,
col_name,
row
);
Err(FitsError::ReadCellString {
fits_filename: fits_fptr.file_path().to_path_buf(),
hdu_num: fits_table_hdu.number + 1,
row_num: row,
col_name: col_name.to_string(),
})
}
}
}
}
pub fn get_table_col_index(
fits_fptr: &mut FitsFile,
fits_table_hdu: &fitsio::hdu::FitsHdu,
col_name: &str,
) -> Result<i32, FitsError> {
unsafe {
let mut status = 0;
let mut col_num = -1;
let keyword = std::ffi::CString::new(col_name).unwrap().into_raw();
fitsio_sys::ffgcno(fits_fptr.as_raw(), 0, keyword, &mut col_num, &mut status);
drop(std::ffi::CString::from_raw(keyword));
if status != 0 {
return Err(FitsError::TableColNotFound {
fits_filename: fits_fptr.file_path().to_path_buf(),
hdu_num: fits_table_hdu.number,
col_name: col_name.to_string(),
});
}
Ok(col_num)
}
}
#[macro_export]
macro_rules! get_optional_fits_key {
($fptr:expr, $hdu:expr, $keyword:expr) => {
$crate::_get_optional_fits_key($fptr, $hdu, $keyword, file!(), line!())
};
}