astro_rs/fits/header_value/
mod.rs

1mod bitpix;
2mod tform;
3
4pub use bitpix::*;
5pub use tform::*;
6
7use std::any::{Any, TypeId};
8use std::fmt::Debug;
9
10use super::header::FitsHeaderError;
11
12/// A trait that allows data to be serialized/deserialized as a FITS header value.
13pub trait FitsHeaderValue: Debug + RealAny {
14    /// Attempts to deserialize a value from the given bytes. The given bytes shall not be padded by spaces.
15    fn from_bytes(raw: Vec<u8>) -> Result<Self, FitsHeaderError>
16    where
17        Self: Sized;
18
19    /// Serializes the value to bytes. The bytes shall include padding spaces.
20    fn to_bytes(&self) -> [u8; 70];
21}
22
23// credit to https://github.com/chris-morgan/mopa for this solution
24impl dyn FitsHeaderValue {
25    /// Determines if the type of `self` is the same as `T`.
26    pub fn is<T: FitsHeaderValue + 'static>(&self) -> bool {
27        TypeId::of::<T>() == RealAny::real_type_id(self)
28    }
29}
30
31/// A trait used to get the real type ID for implementors of `FitsHeaderValue`.
32pub trait RealAny {
33    /// Gets the base type ID for `self`.
34    fn real_type_id(&self) -> TypeId;
35}
36
37impl<T: Any> RealAny for T {
38    fn real_type_id(&self) -> TypeId {
39        TypeId::of::<T>()
40    }
41}
42
43/// ```
44/// use astro_rs::fits::FitsHeaderError;
45/// use astro_rs::fits::FitsHeaderValue;
46///
47/// // successful deserialization
48/// let true_value: bool = FitsHeaderValue::from_bytes(b"T".to_vec())?;
49/// assert!(true_value);
50/// let false_value: bool = FitsHeaderValue::from_bytes(b"F".to_vec())?;
51/// assert!(!false_value);
52///
53/// // failed deserialization
54/// let result: Result<bool, FitsHeaderError> = FitsHeaderValue::from_bytes(b"A".to_vec());
55/// assert!(result.is_err());
56/// let result: Result<bool, FitsHeaderError> = FitsHeaderValue::from_bytes(b"true".to_vec());
57/// assert!(result.is_err());
58///
59/// // serialization
60/// assert_eq!(true_value.to_bytes(), *b"                   T                                                  ");
61/// assert_eq!(false_value.to_bytes(), *b"                   F                                                  ");
62/// # Ok::<(), astro_rs::fits::FitsHeaderError>(())
63/// ```
64impl FitsHeaderValue for bool {
65    fn from_bytes(raw: Vec<u8>) -> Result<Self, FitsHeaderError> {
66        if raw.len() == 1 {
67            match *raw.first().unwrap() {
68                b'T' => Ok(true),
69                b'F' => Ok(false),
70                _ => Err(FitsHeaderError::DeserializationError {
71                    found: raw,
72                    intent: String::from("header card bool value"),
73                }),
74            }
75        } else {
76            Err(FitsHeaderError::InvalidLength {
77                expected: 1,
78                found: raw.len(),
79                intent: String::from("header card u8 value"),
80            })
81        }
82    }
83
84    fn to_bytes(&self) -> [u8; 70] {
85        let mut result = [b' '; 70];
86        if *self {
87            result[19] = b'T';
88        } else {
89            result[19] = b'F';
90        }
91        result
92    }
93}
94
95/// ```
96/// use astro_rs::fits::FitsHeaderError;
97/// use astro_rs::fits::FitsHeaderValue;
98///
99/// // successful deserialization
100/// let max_value: u8 = FitsHeaderValue::from_bytes(b"255".to_vec())?;
101/// assert_eq!(max_value, 255);
102/// let min_value: u8 = FitsHeaderValue::from_bytes(b"0".to_vec())?;
103/// assert_eq!(min_value, 0);
104///
105/// // failed deserialization
106/// let result: Result<u8, FitsHeaderError> = FitsHeaderValue::from_bytes(b"300".to_vec());
107/// assert!(result.is_err());
108/// let result: Result<u8, FitsHeaderError> = FitsHeaderValue::from_bytes(b"Not a number".to_vec());
109/// assert!(result.is_err());
110///
111/// // serialization
112/// assert_eq!(max_value.to_bytes(), *b"                 255                                                  ");
113/// assert_eq!(min_value.to_bytes(), *b"                   0                                                  ");
114/// # Ok::<(), astro_rs::fits::FitsHeaderError>(())
115/// ```
116impl FitsHeaderValue for u8 {
117    fn from_bytes(raw: Vec<u8>) -> Result<Self, FitsHeaderError> {
118        let value_string =
119            String::from_utf8(raw).map_err(|er| FitsHeaderError::DeserializationError {
120                found: er.into_bytes(),
121                intent: String::from("header card u8 value"),
122            })?;
123        value_string
124            .parse()
125            .map_err(|_| FitsHeaderError::DeserializationError {
126                found: value_string.into_bytes(),
127                intent: String::from("header card u8 value"),
128            })
129    }
130
131    fn to_bytes(&self) -> [u8; 70] {
132        let mut result = [b' '; 70];
133        let value_raw = self.to_string().into_bytes();
134        let start = 20 - value_raw.len();
135        for (i, b) in value_raw.iter().enumerate() {
136            result[start + i] = *b;
137        }
138        result
139    }
140}
141
142/// ```
143/// use astro_rs::fits::FitsHeaderError;
144/// use astro_rs::fits::FitsHeaderValue;
145///
146/// // successful deserialization
147/// let max_value: u16 = FitsHeaderValue::from_bytes(b"65535".to_vec())?;
148/// assert_eq!(max_value, 65535);
149/// let min_value: u16 = FitsHeaderValue::from_bytes(b"0".to_vec())?;
150/// assert_eq!(min_value, 0);
151///
152/// // failed deserialization
153/// let result: Result<u16, FitsHeaderError> = FitsHeaderValue::from_bytes(b"66000".to_vec());
154/// assert!(result.is_err());
155/// let result: Result<u16, FitsHeaderError> = FitsHeaderValue::from_bytes(b"Not a number".to_vec());
156/// assert!(result.is_err());
157///
158/// // serialization
159/// assert_eq!(max_value.to_bytes(), *b"               65535                                                  ");
160/// assert_eq!(min_value.to_bytes(), *b"                   0                                                  ");
161/// # Ok::<(), astro_rs::fits::FitsHeaderError>(())
162/// ```
163impl FitsHeaderValue for u16 {
164    fn from_bytes(raw: Vec<u8>) -> Result<Self, FitsHeaderError> {
165        let value_string =
166            String::from_utf8(raw).map_err(|er| FitsHeaderError::DeserializationError {
167                found: er.into_bytes(),
168                intent: String::from("header card u16 value"),
169            })?;
170        value_string
171            .parse()
172            .map_err(|_| FitsHeaderError::DeserializationError {
173                found: value_string.into_bytes(),
174                intent: String::from("header card u16 value"),
175            })
176    }
177
178    fn to_bytes(&self) -> [u8; 70] {
179        let mut result = [b' '; 70];
180        let value_raw = self.to_string().into_bytes();
181        let start = 20 - value_raw.len();
182        for (i, b) in value_raw.iter().enumerate() {
183            result[start + i] = *b;
184        }
185        result
186    }
187}
188
189/// ```
190/// use astro_rs::fits::FitsHeaderError;
191/// use astro_rs::fits::FitsHeaderValue;
192///
193/// // successful deserialization
194/// let max_value: u32 = FitsHeaderValue::from_bytes(b"4294967295".to_vec())?;
195/// assert_eq!(max_value, 4294967295);
196/// let min_value: u32 = FitsHeaderValue::from_bytes(b"0".to_vec())?;
197/// assert_eq!(min_value, 0);
198///
199/// // failed deserialization
200/// let result: Result<u32, FitsHeaderError> = FitsHeaderValue::from_bytes(b"4300000000".to_vec());
201/// assert!(result.is_err());
202/// let result: Result<u32, FitsHeaderError> = FitsHeaderValue::from_bytes(b"Not a number".to_vec());
203/// assert!(result.is_err());
204///
205/// // serialization
206/// assert_eq!(max_value.to_bytes(), *b"          4294967295                                                  ");
207/// assert_eq!(min_value.to_bytes(), *b"                   0                                                  ");
208/// # Ok::<(), astro_rs::fits::FitsHeaderError>(())
209/// ```
210impl FitsHeaderValue for u32 {
211    fn from_bytes(raw: Vec<u8>) -> Result<Self, FitsHeaderError> {
212        let value_string =
213            String::from_utf8(raw).map_err(|er| FitsHeaderError::DeserializationError {
214                found: er.into_bytes(),
215                intent: String::from("header card u32 value"),
216            })?;
217        value_string
218            .parse()
219            .map_err(|_| FitsHeaderError::DeserializationError {
220                found: value_string.into_bytes(),
221                intent: String::from("header card u32 value"),
222            })
223    }
224
225    fn to_bytes(&self) -> [u8; 70] {
226        let mut result = [b' '; 70];
227        let value_raw = self.to_string().into_bytes();
228        let start = 20 - value_raw.len();
229        for (i, b) in value_raw.iter().enumerate() {
230            result[start + i] = *b;
231        }
232        result
233    }
234}
235
236/// ```
237/// use astro_rs::fits::FitsHeaderError;
238/// use astro_rs::fits::FitsHeaderValue;
239///
240/// // successful deserialization
241/// let value: String = FitsHeaderValue::from_bytes(String::from("hello world").into_bytes())?;
242/// assert_eq!(value, String::from("hello world"));
243/// let quote_value: String = FitsHeaderValue::from_bytes(String::from("'this ''includes'' quotes'").into_bytes())?;
244/// assert_eq!(quote_value, String::from("this 'includes' quotes"));
245///
246/// // failed deserialization
247/// let result: Result<String, FitsHeaderError> = FitsHeaderValue::from_bytes(vec![0, 159, 146, 150]);
248/// assert!(result.is_err());
249///
250/// // serialization
251/// assert_eq!(value.to_bytes(), *b"'hello world'                                                         ");
252/// assert_eq!(quote_value.to_bytes(), *b"'this ''includes'' quotes'                                            ");
253/// # Ok::<(), astro_rs::fits::FitsHeaderError>(())
254/// ```
255impl FitsHeaderValue for String {
256    fn from_bytes(mut raw: Vec<u8>) -> Result<Self, FitsHeaderError> {
257        let mut remove_quote = true;
258        let mut i = 0;
259        while i < raw.len() {
260            if raw[i] == b'\'' {
261                if remove_quote {
262                    raw.remove(i);
263                } else {
264                    i += 1;
265                }
266                remove_quote = false;
267            } else {
268                remove_quote = true;
269                i += 1;
270            }
271        }
272        let value = String::from_utf8(raw).map_err(|er| FitsHeaderError::DeserializationError {
273            found: er.into_bytes(),
274            intent: String::from("header card String value"),
275        })?;
276        Ok(value.trim().to_owned())
277    }
278
279    fn to_bytes(&self) -> [u8; 70] {
280        let mut result = [b' '; 70];
281        result[0] = b'\'';
282        let mut num_quotes = 1;
283        let value_raw = self.as_bytes();
284        for (i, b) in value_raw.iter().enumerate() {
285            if *b == b'\'' {
286                result[i + num_quotes] = b'\'';
287                num_quotes += 1;
288            }
289            result[i + num_quotes] = *b;
290        }
291        result[value_raw.len() + num_quotes] = b'\'';
292        result
293    }
294}