1use std::ffi::{CStr, CString};
16use std::os::raw::{c_char, c_int, c_void};
17use std::path::Path;
18use std::ptr;
19
20use fitsio::FitsFile;
21use fitsio::errors::check_status;
22use fitsio::sys::{LONGLONG, fitsfile};
23
24use crate::error::{AtfitsError, Result};
25use crate::image::is_structural_keyword;
26
27pub struct CubeLayout {
30 pub header: Vec<u8>,
32 pub datastart: u64,
34}
35
36pub fn create_mem_cube(template: &Path, bitpix: i64, dims: &[usize]) -> Result<FitsFile> {
43 let mut status = 0;
44 let memname = CString::new("mem://").expect("static name has no NUL");
45 let mut raw: *mut fitsfile = ptr::null_mut();
46 unsafe {
47 fitsio::sys::ffinit(&mut raw, memname.as_ptr(), &mut status);
48 }
49 check_status(status)?;
50
51 let mut naxes: Vec<LONGLONG> = dims.iter().map(|&d| d as LONGLONG).collect();
53 unsafe {
54 fitsio::sys::ffcrimll(
55 raw,
56 bitpix as c_int,
57 naxes.len() as c_int,
58 naxes.as_mut_ptr(),
59 &mut status,
60 );
61 }
62 if let Err(e) = check_status(status) {
63 let mut close = 0;
64 unsafe { fitsio::sys::ffclos(raw, &mut close) };
65 return Err(e.into());
66 }
67
68 let mut in_fptr = FitsFile::open(template.to_string_lossy().as_ref())?;
70 in_fptr.primary_hdu()?;
71 unsafe {
72 let mut nkeys: c_int = 0;
73 let mut morekeys: c_int = 0;
74 fitsio::sys::ffghsp(in_fptr.as_raw(), &mut nkeys, &mut morekeys, &mut status);
75 check_status(status)?;
76
77 let mut card = [0 as c_char; 81];
79 for i in 1..=nkeys {
80 card.fill(0);
81 fitsio::sys::ffgrec(in_fptr.as_raw(), i, card.as_mut_ptr(), &mut status);
82 if check_status(status).is_err() {
83 break;
84 }
85 let card_str = CStr::from_ptr(card.as_ptr()).to_string_lossy();
86 let name = card_str.split([' ', '=']).next().unwrap_or("").trim();
87 if is_structural_keyword(name) {
88 continue;
89 }
90 fitsio::sys::ffprec(raw, card.as_ptr(), &mut status);
91 check_status(status)?;
92 }
93 }
94
95 let handle = unsafe { FitsFile::from_raw(raw, fitsio::FileOpenMode::READWRITE)? };
96 Ok(handle)
97}
98
99pub fn extract_header_layout(fptr: &mut FitsFile) -> Result<CubeLayout> {
102 fptr.primary_hdu()?; let raw = unsafe { fptr.as_raw() };
104 let mut status = 0;
105
106 let mut headstart: LONGLONG = 0;
107 let mut datastart: LONGLONG = 0;
108 let mut dataend: LONGLONG = 0;
109 unsafe {
110 fitsio::sys::ffghadll(
111 raw,
112 &mut headstart,
113 &mut datastart,
114 &mut dataend,
115 &mut status,
116 );
117 }
118 check_status(status)?;
119
120 let mut header_ptr: *mut c_char = ptr::null_mut();
123 let mut nkeys: c_int = 0;
124 unsafe {
125 fitsio::sys::ffhdr2str(
126 raw,
127 0, ptr::null_mut(),
129 0,
130 &mut header_ptr,
131 &mut nkeys,
132 &mut status,
133 );
134 }
135 check_status(status)?;
136
137 let mut header = unsafe {
138 let bytes = CStr::from_ptr(header_ptr).to_bytes().to_vec();
139 let mut free_status = 0;
140 fitsio::sys::fffree(header_ptr as *mut c_void, &mut free_status);
141 bytes
142 };
143
144 let datastart = datastart as u64;
145 if header.len() as u64 + 3 > datastart {
146 return Err(AtfitsError::Other(format!(
147 "header ({} bytes) does not fit before data unit ({datastart} bytes)",
148 header.len()
149 )));
150 }
151 header.extend_from_slice(b"END");
154 header.resize(datastart as usize, b' ');
155
156 Ok(CubeLayout { header, datastart })
157}