1pub(crate) mod compression;
91pub mod error;
92pub mod formats;
93pub(crate) mod io;
94pub mod record;
95pub mod types;
96
97pub use crate::error::DmapError;
98pub use crate::formats::dmap::DmapRecord;
99pub use crate::formats::fitacf::FitacfRecord;
100pub use crate::formats::grid::GridRecord;
101pub use crate::formats::iqdat::IqdatRecord;
102pub use crate::formats::map::MapRecord;
103pub use crate::formats::rawacf::RawacfRecord;
104pub use crate::formats::snd::SndRecord;
105pub use crate::record::Record;
106use crate::types::DmapField;
107use indexmap::IndexMap;
108use paste::paste;
109use pyo3::prelude::*;
110use pyo3::types::PyBytes;
111use std::path::{Path, PathBuf};
112
113macro_rules! write_rust {
115 ($type:ident) => {
116 paste! {
117 #[doc = "Attempts to convert `recs` to `" $type:camel Record "` then append to `outfile`."]
118 #[doc = ""]
119 #[doc = "# Errors"]
120 #[doc = "if any of the `IndexMap`s are unable to be interpreted as a `" $type:camel Record "`, or there is an issue writing to file."]
121 pub fn [< try_write_ $type >]<P: AsRef<Path>>(
122 recs: Vec<IndexMap<String, DmapField>>,
123 outfile: P,
124 ) -> Result<(), DmapError> {
125 let bytes = [< $type:camel Record >]::try_into_bytes(recs)?;
126 crate::io::bytes_to_file(bytes, outfile).map_err(DmapError::from)
127 }
128 }
129 }
130}
131
132write_rust!(iqdat);
133write_rust!(rawacf);
134write_rust!(fitacf);
135write_rust!(grid);
136write_rust!(map);
137write_rust!(snd);
138write_rust!(dmap);
139
140macro_rules! read_py {
151 (
152 $name:ident,
153 $py_name:literal,
154 $lax_name:literal,
155 $bytes_name:literal,
156 $lax_bytes_name:literal,
157 $sniff_name:literal,
158 $metadata_name:literal
159 ) => {
160 paste! {
161 #[doc = "Reads a `" $name:upper "` file, returning a list of dictionaries containing the fields." ]
162 #[pyfunction]
163 #[pyo3(name = $py_name)]
164 #[pyo3(text_signature = "(infile: str, /)")]
165 fn [< read_ $name _py >](infile: PathBuf) -> PyResult<Vec<IndexMap<String, DmapField>>> {
166 Ok([< $name:camel Record >]::read_file(&infile)
167 .map_err(PyErr::from)?
168 .into_iter()
169 .map(|rec| rec.inner())
170 .collect()
171 )
172 }
173
174 #[doc = "Reads a `" $name:upper "` file, returning a tuple of" ]
175 #[doc = "(list of dictionaries containing the fields, byte where first corrupted record starts). "]
176 #[pyfunction]
177 #[pyo3(name = $lax_name)]
178 #[pyo3(text_signature = "(infile: str, /)")]
179 fn [< read_ $name _lax_py >](
180 infile: PathBuf,
181 ) -> PyResult<(Vec<IndexMap<String, DmapField>>, Option<usize>)> {
182 let result = [< $name:camel Record >]::read_file_lax(&infile).map_err(PyErr::from)?;
183 Ok((
184 result.0.into_iter().map(|rec| rec.inner()).collect(),
185 result.1,
186 ))
187 }
188
189 #[doc = "Read in `" $name:upper "` records from bytes, returning `List[Dict]` of the records." ]
190 #[pyfunction]
191 #[pyo3(name = $bytes_name)]
192 #[pyo3(text_signature = "(buf: bytes, /)")]
193 fn [< read_ $name _bytes_py >](bytes: &[u8]) -> PyResult<Vec<IndexMap<String, DmapField>>> {
194 Ok([< $name:camel Record >]::read_records(bytes)?
195 .into_iter()
196 .map(|rec| rec.inner())
197 .collect()
198 )
199 }
200
201 #[doc = "Reads a `" $name:upper "` file, returning a tuple of" ]
202 #[doc = "(list of dictionaries containing the fields, byte where first corrupted record starts). "]
203 #[pyfunction]
204 #[pyo3(name = $lax_bytes_name)]
205 #[pyo3(text_signature = "(buf: bytes, /)")]
206 fn [< read_ $name _bytes_lax_py >](
207 bytes: &[u8],
208 ) -> PyResult<(Vec<IndexMap<String, DmapField>>, Option<usize>)> {
209 let result = [< $name:camel Record >]::read_records_lax(bytes).map_err(PyErr::from)?;
210 Ok((
211 result.0.into_iter().map(|rec| rec.inner()).collect(),
212 result.1,
213 ))
214 }
215
216 #[doc = "Reads a `" $name:upper "` file, returning the first record." ]
217 #[pyfunction]
218 #[pyo3(name = $sniff_name)]
219 #[pyo3(text_signature = "(infile: str, /)")]
220 fn [< sniff_ $name _py >](infile: PathBuf) -> PyResult<IndexMap<String, DmapField>> {
221 Ok([< $name:camel Record >]::sniff_file(&infile)
222 .map_err(PyErr::from)?
223 .inner()
224 )
225 }
226
227 #[doc = "Reads a `" $name:upper "` file, returning a list of dictionaries containing the only the metadata fields." ]
228 #[pyfunction]
229 #[pyo3(name = $metadata_name)]
230 #[pyo3(text_signature = "(infile: str, /)")]
231 fn [< read_ $name _metadata_py >](infile: PathBuf) -> PyResult<Vec<IndexMap<String, DmapField>>> {
232 Ok([< $name:camel Record >]::read_file_metadata(&infile)
233 .map_err(PyErr::from)?
234 )
235 }
236 }
237 }
238}
239
240read_py!(
241 iqdat,
242 "read_iqdat",
243 "read_iqdat_lax",
244 "read_iqdat_bytes",
245 "read_iqdat_bytes_lax",
246 "sniff_iqdat",
247 "read_iqdat_metadata"
248);
249read_py!(
250 rawacf,
251 "read_rawacf",
252 "read_rawacf_lax",
253 "read_rawacf_bytes",
254 "read_rawacf_bytes_lax",
255 "sniff_rawacf",
256 "read_rawacf_metadata"
257);
258read_py!(
259 fitacf,
260 "read_fitacf",
261 "read_fitacf_lax",
262 "read_fitacf_bytes",
263 "read_fitacf_bytes_lax",
264 "sniff_fitacf",
265 "read_fitacf_metadata"
266);
267read_py!(
268 grid,
269 "read_grid",
270 "read_grid_lax",
271 "read_grid_bytes",
272 "read_grid_bytes_lax",
273 "sniff_grid",
274 "read_grid_metadata"
275);
276read_py!(
277 map,
278 "read_map",
279 "read_map_lax",
280 "read_map_bytes",
281 "read_map_bytes_lax",
282 "sniff_map",
283 "read_map_metadata"
284);
285read_py!(
286 snd,
287 "read_snd",
288 "read_snd_lax",
289 "read_snd_bytes",
290 "read_snd_bytes_lax",
291 "sniff_snd",
292 "read_snd_metadata"
293);
294read_py!(
295 dmap,
296 "read_dmap",
297 "read_dmap_lax",
298 "read_dmap_bytes",
299 "read_dmap_bytes_lax",
300 "sniff_dmap",
301 "read_dmap_metadata"
302);
303
304#[pyfunction]
310#[pyo3(name = "write_dmap")]
311#[pyo3(text_signature = "(recs: list[dict], outfile: str, /)")]
312fn write_dmap_py(recs: Vec<IndexMap<String, DmapField>>, outfile: PathBuf) -> PyResult<()> {
313 try_write_dmap(recs, &outfile).map_err(PyErr::from)
314}
315
316#[pyfunction]
323#[pyo3(name = "write_dmap_bytes")]
324#[pyo3(text_signature = "(recs: list[dict], /)")]
325fn write_dmap_bytes_py(py: Python, recs: Vec<IndexMap<String, DmapField>>) -> PyResult<Py<PyAny>> {
326 let bytes = DmapRecord::try_into_bytes(recs).map_err(PyErr::from)?;
327 Ok(PyBytes::new(py, &bytes).into())
328}
329
330macro_rules! write_py {
332 ($name:ident, $fn_name:literal, $bytes_name:literal) => {
333 paste! {
334 #[doc = "Checks that a list of dictionaries contains valid `" $name:upper "` records, then appends to outfile." ]
335 #[pyfunction]
336 #[pyo3(name = $fn_name)]
337 #[pyo3(text_signature = "(recs: list[dict], outfile: str, /)")]
338 fn [< write_ $name _py >](recs: Vec<IndexMap<String, DmapField>>, outfile: PathBuf) -> PyResult<()> {
339 [< try_write_ $name >](recs, &outfile).map_err(PyErr::from)
340 }
341
342 #[doc = "Checks that a list of dictionaries contains valid `" $name:upper "` records, then converts them to bytes." ]
343 #[doc = "Returns `list[bytes]`, one entry per record." ]
344 #[pyfunction]
345 #[pyo3(name = $bytes_name)]
346 #[pyo3(text_signature = "(recs: list[dict], /)")]
347 fn [< write_ $name _bytes_py >](py: Python, recs: Vec<IndexMap<String, DmapField>>) -> PyResult<Py<PyAny>> {
348 let bytes = [< $name:camel Record >]::try_into_bytes(recs).map_err(PyErr::from)?;
349 Ok(PyBytes::new(py, &bytes).into())
350 }
351 }
352 }
353}
354
355write_py!(iqdat, "write_iqdat", "write_iqdat_bytes");
357write_py!(rawacf, "write_rawacf", "write_rawacf_bytes");
358write_py!(fitacf, "write_fitacf", "write_fitacf_bytes");
359write_py!(grid, "write_grid", "write_grid_bytes");
360write_py!(map, "write_map", "write_map_bytes");
361write_py!(snd, "write_snd", "write_snd_bytes");
362
363#[pymodule]
365fn dmap_rs(m: &Bound<'_, PyModule>) -> PyResult<()> {
366 m.add_function(wrap_pyfunction!(read_dmap_py, m)?)?;
368 m.add_function(wrap_pyfunction!(read_iqdat_py, m)?)?;
369 m.add_function(wrap_pyfunction!(read_rawacf_py, m)?)?;
370 m.add_function(wrap_pyfunction!(read_fitacf_py, m)?)?;
371 m.add_function(wrap_pyfunction!(read_snd_py, m)?)?;
372 m.add_function(wrap_pyfunction!(read_grid_py, m)?)?;
373 m.add_function(wrap_pyfunction!(read_map_py, m)?)?;
374
375 m.add_function(wrap_pyfunction!(read_dmap_lax_py, m)?)?;
377 m.add_function(wrap_pyfunction!(read_iqdat_lax_py, m)?)?;
378 m.add_function(wrap_pyfunction!(read_rawacf_lax_py, m)?)?;
379 m.add_function(wrap_pyfunction!(read_fitacf_lax_py, m)?)?;
380 m.add_function(wrap_pyfunction!(read_snd_lax_py, m)?)?;
381 m.add_function(wrap_pyfunction!(read_grid_lax_py, m)?)?;
382 m.add_function(wrap_pyfunction!(read_map_lax_py, m)?)?;
383
384 m.add_function(wrap_pyfunction!(read_dmap_bytes_py, m)?)?;
386 m.add_function(wrap_pyfunction!(read_iqdat_bytes_py, m)?)?;
387 m.add_function(wrap_pyfunction!(read_rawacf_bytes_py, m)?)?;
388 m.add_function(wrap_pyfunction!(read_fitacf_bytes_py, m)?)?;
389 m.add_function(wrap_pyfunction!(read_snd_bytes_py, m)?)?;
390 m.add_function(wrap_pyfunction!(read_grid_bytes_py, m)?)?;
391 m.add_function(wrap_pyfunction!(read_map_bytes_py, m)?)?;
392
393 m.add_function(wrap_pyfunction!(read_dmap_bytes_lax_py, m)?)?;
395 m.add_function(wrap_pyfunction!(read_iqdat_bytes_lax_py, m)?)?;
396 m.add_function(wrap_pyfunction!(read_rawacf_bytes_lax_py, m)?)?;
397 m.add_function(wrap_pyfunction!(read_fitacf_bytes_lax_py, m)?)?;
398 m.add_function(wrap_pyfunction!(read_snd_bytes_lax_py, m)?)?;
399 m.add_function(wrap_pyfunction!(read_grid_bytes_lax_py, m)?)?;
400 m.add_function(wrap_pyfunction!(read_map_bytes_lax_py, m)?)?;
401
402 m.add_function(wrap_pyfunction!(write_dmap_py, m)?)?;
404 m.add_function(wrap_pyfunction!(write_iqdat_py, m)?)?;
405 m.add_function(wrap_pyfunction!(write_rawacf_py, m)?)?;
406 m.add_function(wrap_pyfunction!(write_fitacf_py, m)?)?;
407 m.add_function(wrap_pyfunction!(write_grid_py, m)?)?;
408 m.add_function(wrap_pyfunction!(write_map_py, m)?)?;
409 m.add_function(wrap_pyfunction!(write_snd_py, m)?)?;
410
411 m.add_function(wrap_pyfunction!(write_dmap_bytes_py, m)?)?;
413 m.add_function(wrap_pyfunction!(write_iqdat_bytes_py, m)?)?;
414 m.add_function(wrap_pyfunction!(write_rawacf_bytes_py, m)?)?;
415 m.add_function(wrap_pyfunction!(write_fitacf_bytes_py, m)?)?;
416 m.add_function(wrap_pyfunction!(write_snd_bytes_py, m)?)?;
417 m.add_function(wrap_pyfunction!(write_grid_bytes_py, m)?)?;
418 m.add_function(wrap_pyfunction!(write_map_bytes_py, m)?)?;
419
420 m.add_function(wrap_pyfunction!(sniff_dmap_py, m)?)?;
422 m.add_function(wrap_pyfunction!(sniff_iqdat_py, m)?)?;
423 m.add_function(wrap_pyfunction!(sniff_rawacf_py, m)?)?;
424 m.add_function(wrap_pyfunction!(sniff_fitacf_py, m)?)?;
425 m.add_function(wrap_pyfunction!(sniff_snd_py, m)?)?;
426 m.add_function(wrap_pyfunction!(sniff_grid_py, m)?)?;
427 m.add_function(wrap_pyfunction!(sniff_map_py, m)?)?;
428
429 m.add_function(wrap_pyfunction!(read_dmap_metadata_py, m)?)?;
431 m.add_function(wrap_pyfunction!(read_iqdat_metadata_py, m)?)?;
432 m.add_function(wrap_pyfunction!(read_rawacf_metadata_py, m)?)?;
433 m.add_function(wrap_pyfunction!(read_fitacf_metadata_py, m)?)?;
434 m.add_function(wrap_pyfunction!(read_snd_metadata_py, m)?)?;
435 m.add_function(wrap_pyfunction!(read_grid_metadata_py, m)?)?;
436 m.add_function(wrap_pyfunction!(read_map_metadata_py, m)?)?;
437
438 Ok(())
439}