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}