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}