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}