Skip to main content

xisf_header/
keyword.rs

1//! The [`FitsKeyword`] record.
2
3use crate::value::{FromField, IntoValue, Value};
4
5/// A single FITS keyword extracted from (or destined for) an XISF header:
6/// a name, a value, and an optional comment.
7///
8/// The value's on-disk kind (quoted string vs. bare literal) is preserved; see
9/// [`IntoValue`] for how the kind is chosen when you write one.
10///
11/// ```
12/// use xisf_header::FitsKeyword;
13///
14/// let kw = FitsKeyword::new("IMAGETYP", "Master Dark", "Type of image");
15/// assert_eq!(kw.value_str(), "Master Dark");
16/// assert_eq!(kw.comment, "Type of image");
17/// ```
18#[derive(Debug, Clone, PartialEq, Eq, Default)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub struct FitsKeyword {
21    /// The keyword name (e.g. `EXPTIME`), kept verbatim.
22    pub name: String,
23    /// The keyword value.
24    pub(crate) value: Value,
25    /// The keyword comment (empty when absent).
26    pub comment: String,
27}
28
29impl FitsKeyword {
30    /// Create a keyword from its name, value, and comment.
31    ///
32    /// ```
33    /// use xisf_header::FitsKeyword;
34    /// let kw = FitsKeyword::new("GAIN", 100_i64, "Sensor gain");
35    /// assert_eq!(kw.get::<i64>(), Some(100));
36    /// ```
37    pub fn new(name: impl Into<String>, value: impl IntoValue, comment: impl Into<String>) -> Self {
38        Self {
39            name: name.into(),
40            value: value.into_value(),
41            comment: comment.into(),
42        }
43    }
44
45    /// The value's raw text, regardless of kind.
46    ///
47    /// ```
48    /// use xisf_header::FitsKeyword;
49    ///
50    /// let kw = FitsKeyword::new("OBJECT", "NGC 7000", "Target");
51    /// assert_eq!(kw.value_str(), "NGC 7000");
52    /// ```
53    #[must_use]
54    pub fn value_str(&self) -> &str {
55        self.value.text()
56    }
57
58    /// Interpret the value as `T` (see [`FromField`]).
59    ///
60    /// ```
61    /// use xisf_header::FitsKeyword;
62    ///
63    /// let kw = FitsKeyword::new("EXPTIME", 300.0, "");
64    /// assert_eq!(kw.get::<f64>(), Some(300.0));
65    /// ```
66    #[must_use]
67    pub fn get<T: FromField>(&self) -> Option<T> {
68        T::from_field(self.value.text())
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn new_selects_value_kind_from_rust_type() {
78        let s = FitsKeyword::new("OBJECT", "M31", "target");
79        assert_eq!(s.value_str(), "M31");
80        assert_eq!(s.comment, "target");
81        assert_eq!(s.get::<String>(), Some("M31".to_owned()));
82
83        let n = FitsKeyword::new("GAIN", 100_i64, "");
84        assert_eq!(n.value_str(), "100");
85        assert_eq!(n.get::<i64>(), Some(100));
86        assert_eq!(n.get::<bool>(), None);
87    }
88}