Skip to main content

fits_io/hdu/
bin_table_hdu.rs

1use crate::bin_table::BinTable;
2#[cfg(feature = "tokio")]
3use crate::bin_table::OwnedRow;
4use crate::hdu::HDU;
5use crate::image::Image;
6#[cfg(feature = "serde")]
7use serde::Serialize;
8#[cfg(feature = "serde")]
9use serde::de::DeserializeOwned;
10use std::error::Error;
11use std::fmt;
12
13/// An HDU whose data section is a binary table.
14pub trait BinTableHDU: HDU + fmt::Debug + Send + Sync {
15    /// How many bytes the rows occupy, not counting the heap.
16    fn table_data_bytes_len(&self) -> u64;
17
18    /// Reads the whole table into memory.
19    fn read_table(&self) -> Result<BinTable, Box<dyn Error + Send + Sync>>;
20
21    /// Reads every row into `T`, matching columns to fields by their TTYPEn names.
22    #[cfg(feature = "serde")]
23    fn read_rows<T: DeserializeOwned + Send + Sync>(
24        &self,
25    ) -> Result<Vec<T>, Box<dyn Error + Send + Sync>>;
26
27    /// Whether this table holds a compressed image rather than a table of its
28    /// own.
29    ///
30    /// See [`BinTableHDU::read_compressed_image`].
31    fn is_compressed_image(&self) -> bool {
32        self.header().is_compressed_image()
33    }
34
35    /// Decompresses the image this table stands for.
36    ///
37    /// The tiled image convention stores an image as a table, one compressed
38    /// tile per row, which is how `fpack` and most archives distribute large
39    /// images. `None` when this is an ordinary table.
40    ///
41    /// RICE_1, GZIP_1, GZIP_2 and NOCOMPRESS are supported; another algorithm is
42    /// an error rather than an image made of noise.
43    fn read_compressed_image(&self) -> Result<Option<Image>, Box<dyn Error + Send + Sync>> {
44        if !self.is_compressed_image() {
45            return Ok(None);
46        }
47
48        let table = self.read_table()?;
49
50        Ok(Some(crate::image::compression::read_image(
51            self.header(),
52            &table,
53        )?))
54    }
55
56    /// Replaces this HDU's table.
57    ///
58    /// The header is rewritten to describe the new table — its row width and
59    /// count, and a TFORMn and TTYPEn for every column — because those cards are
60    /// the only thing that says how to read the bytes back.
61    fn set_table(&mut self, table: &BinTable) -> Result<(), Box<dyn Error + Send + Sync>>;
62
63    /// Serialises `rows` and stores the result, as [`to_bin_table`] followed by
64    /// [`set_table`].
65    ///
66    /// [`to_bin_table`]: crate::bin_table::to_bin_table
67    /// [`set_table`]: BinTableHDU::set_table
68    #[cfg(feature = "serde")]
69    fn set_rows<T: Serialize>(&mut self, rows: &T) -> Result<(), Box<dyn Error + Send + Sync>> {
70        self.set_table(&crate::bin_table::to_bin_table(rows)?)
71    }
72
73    /// Streams the table one row at a time, without holding the whole thing in
74    /// memory.
75    ///
76    /// Rows arrive in the order they are stored. A row is decoded lazily, so a
77    /// column that cannot be read reports its error from
78    /// [`OwnedRow::get`] rather than from the stream.
79    ///
80    /// A truncated file ends the stream early rather than failing it; compare
81    /// the number of rows received against NAXIS2 to tell the two apart.
82    #[cfg(feature = "tokio")]
83    fn stream_table_rows_raw(
84        &self,
85    ) -> Result<futures::stream::BoxStream<'_, OwnedRow>, Box<dyn Error + Send + Sync>>;
86
87    /// Streams the table as deserialised rows of `T`.
88    ///
89    /// Unlike [`stream_table_rows_raw`] each row is decoded as it arrives, so a
90    /// row that does not fit `T` yields an `Err` in its place and the stream
91    /// carries on.
92    ///
93    /// [`stream_table_rows_raw`]: BinTableHDU::stream_table_rows_raw
94    #[cfg(feature = "serde")]
95    #[cfg(feature = "tokio")]
96    fn stream_table_rows<T: DeserializeOwned + Send + Sync>(
97        &self,
98    ) -> Result<futures::stream::BoxStream<'_, crate::Result<T>>, Box<dyn Error + Send + Sync>>;
99}