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}