Skip to main content

fits_io/bin_table/
value.rs

1/// One entry of one column of a table.
2///
3/// A FITS column holds a run of values rather than a single one, so every
4/// variant here carries a vector even where the run is one long.
5#[derive(Debug, Clone)]
6pub enum Value {
7    /// `rA`: one string of `r` characters.
8    String(String),
9    /// `rAw`: `r / w` strings of `w` characters each.
10    StringArray(Vec<String>),
11    /// `rL`: `r` logicals.
12    Boolean(Vec<bool>),
13    /// `rX`: `r` bits, packed into `ceil(r / 8)` bytes.
14    ///
15    /// The bits stay packed because that is how the column stores them; `len`
16    /// says how many of them are real, since the last byte is padded. Use
17    /// [`Value::bits`] to walk them.
18    Bit {
19        /// The bytes the bits are packed into.
20        bytes: Vec<u8>,
21        /// How many bits of them the column actually holds.
22        len: usize,
23    },
24    /// `rB`: `r` unsigned bytes.
25    U8(Vec<u8>),
26    /// `rS`: `r` signed bytes.
27    I8(Vec<i8>),
28    /// `rU`: `r` unsigned 16-bit integers.
29    U16(Vec<u16>),
30    /// `rI`: `r` signed 16-bit integers.
31    I16(Vec<i16>),
32    /// `rV`: `r` unsigned 32-bit integers.
33    U32(Vec<u32>),
34    /// `rJ`: `r` signed 32-bit integers.
35    I32(Vec<i32>),
36    /// `rK`: `r` signed 64-bit integers.
37    I64(Vec<i64>),
38    /// `rK` with a TZEROn of 2^63: the unsigned reading of a signed column.
39    U64(Vec<u64>),
40    /// `rE`: `r` single precision floats.
41    F32(Vec<f32>),
42    /// `rD`: `r` double precision floats.
43    F64(Vec<f64>),
44    /// `rC`: single precision complex values, as `(real, imaginary)` pairs.
45    C32(Vec<(f32, f32)>),
46    /// `rM`: double precision complex values, as `(real, imaginary)` pairs.
47    M64(Vec<(f64, f64)>),
48    /// Every element of this entry is the TNULLn value, so the entry is
49    /// undefined. Deserialising it into an `Option<T>` field yields `None`.
50    Null,
51}
52
53impl Value {
54    /// Returns some if the value is a string
55    pub fn as_string(&self) -> Option<&String> {
56        if let Value::String(s) = self {
57            Some(s)
58        } else {
59            None
60        }
61    }
62    /// The values if this is a double precision column, or `None` if it is not.
63    pub fn as_f64(&self) -> Option<&Vec<f64>> {
64        if let Value::F64(f) = self {
65            Some(f)
66        } else {
67            None
68        }
69    }
70    /// The values if this is a single precision column, or `None` if it is not.
71    pub fn as_f32(&self) -> Option<&Vec<f32>> {
72        if let Value::F32(f) = self {
73            Some(f)
74        } else {
75            None
76        }
77    }
78    /// The bits of an `rX` column, most significant first, or `None` for any
79    /// other column.
80    ///
81    /// The padding in the last byte is left out, so this yields exactly the `r`
82    /// bits the column declares.
83    pub fn bits(&self) -> Option<impl Iterator<Item = bool> + '_> {
84        let Value::Bit { bytes, len } = self else {
85            return None;
86        };
87
88        Some((0..*len).map(move |bit| {
89            let byte = bytes.get(bit / 8).copied().unwrap_or(0);
90
91            // Bit 0 is the top of the first byte.
92            byte & (1 << (7 - bit % 8)) != 0
93        }))
94    }
95
96    /// Whether this entry is undefined, per its column's TNULLn card.
97    pub fn is_null(&self) -> bool {
98        matches!(self, Value::Null)
99    }
100
101    /// The first value if this is a 64-bit integer column, or `None` if it is not.
102    pub fn as_i64(&self) -> Option<i64> {
103        if let Value::I64(f) = self {
104            f.first().cloned()
105        } else {
106            None
107        }
108    }
109}