Skip to main content

astrors_fork/io/header/
card.rs

1use std::io::Write;
2
3#[derive(Debug, PartialEq, Clone)]
4pub struct Card {
5    pub keyword: String,
6    pub value: CardValue,
7    pub comment: Option<String>,
8}
9
10#[derive(Debug, PartialEq, Clone)]
11pub enum CardValue {
12    INT(i64),
13    FLOAT(f64),
14    STRING(String),
15    LOGICAL(bool),
16    EMPTY,
17}
18
19impl CardValue {
20    pub fn as_int(&self) -> Option<i64> {
21        if let CardValue::INT(value) = self {
22            Some(*value)
23        } else {
24            None
25        }
26    }
27
28    pub fn as_float(&self) -> Option<f64> {
29        if let CardValue::FLOAT(value) = self {
30            Some(*value)
31        } else {
32            None
33        }
34    }
35
36    pub fn as_bool(&self) -> Option<bool> {
37        if let CardValue::LOGICAL(value) = self {
38            Some(*value)
39        } else {
40            None
41        }
42    }
43
44    pub fn to_string(&self) -> String {
45        match self {
46            CardValue::INT(value) => value.to_string(),
47            CardValue::FLOAT(value) => value.to_string(),
48            CardValue::STRING(value) => value.clone(),
49            CardValue::LOGICAL(value) => value.to_string(),
50            CardValue::EMPTY => "".to_string(),
51        }
52    }
53}
54
55fn check_type(s: &str) -> CardValue {
56    if s.parse::<i64>().is_ok() {
57        CardValue::INT(s.parse::<i64>().unwrap())
58    } else if s.parse::<f64>().is_ok() {
59        CardValue::FLOAT(s.parse::<f64>().unwrap())
60    } else if s == "T" || s == "F" || s.parse::<bool>().is_ok() {
61        if s == "T" {
62            CardValue::LOGICAL(true)
63        } else if s == "F" {
64            CardValue::LOGICAL(false)
65        } else {
66            CardValue::LOGICAL(s.parse::<bool>().unwrap())
67        }
68    } else {
69        CardValue::STRING(s.to_string())
70    }
71}
72
73impl Default for Card {
74    fn default() -> Self {
75        Card {
76            keyword: "".to_string(),
77            value: CardValue::EMPTY,
78            comment: None,
79        }
80    }
81}
82
83impl Card {
84    pub fn new(keyword: String, value: String, comment: Option<String>) -> Self {
85        Card {
86            keyword: keyword,
87            value: check_type(&value), // Assuming value is always a string
88            comment: comment,
89        }
90    }
91
92    // Function to set the value and take ownership of the passed variable
93    pub fn set_value(&mut self, new_value: String) {
94        self.value = check_type(&new_value);
95    }
96
97    pub fn get_value_clone(&self) -> String {
98        self.value.to_string()
99    }
100
101    // Function to set the comment and take ownership of the passed variable
102    pub fn set_comment(&mut self, new_comment: String) {
103        self.comment = Some(new_comment);
104    }
105
106    pub fn get_comment_clone(&self) -> String {
107        self.comment.clone().unwrap_or("".to_string())
108    }
109
110    // Function to set the keyword and take ownership of the passed variable
111    pub fn set_keyword(&mut self, new_keyword: String) {
112        self.keyword = new_keyword;
113    }
114
115    pub fn get_keyword_clone(&self) -> String {
116        self.keyword.clone()
117    }
118
119    fn write_formatted_string<W: Write>(
120        &self,
121        writer: &mut W,
122        mut string: String,
123        bytes_count: &mut i32,
124    ) -> std::io::Result<()> {
125        string.truncate(80);
126        string.push_str(&" ".repeat(80 - string.len()));
127        *bytes_count += 80;
128        writer.write_all(string.as_bytes())
129    }
130
131    pub fn write_to<W: Write>(&self, writer: &mut W, bytes_count: &mut i32) -> std::io::Result<()> {
132        if self.keyword == "COMMENT" || self.keyword == "HISTORY" || self.value == CardValue::EMPTY
133        {
134            self.write_formatted_string(writer, format!("{:<80}", self.keyword), bytes_count)
135        } else {
136            let keyword_string = if self.keyword.len() > 8 {
137                format!("HIERARCH {:} = ", self.keyword)
138            } else {
139                format!("{:8}= ", self.keyword)
140            };
141
142            match self.value {
143                CardValue::STRING(_) => self.write_string_card(writer, keyword_string, bytes_count),
144                _ => self.write_other_card(writer, keyword_string, bytes_count),
145            }
146        }
147    }
148
149    pub fn keyword_ref(&self) -> &str {
150        self.keyword.as_ref()
151    }
152
153    pub fn comment_ref(&self) -> &str {
154        self.comment.as_ref().unwrap()
155    }
156
157    fn write_string_card<W: Write>(
158        &self,
159        writer: &mut W,
160        keyword_string: String,
161        bytes_count: &mut i32,
162    ) -> std::io::Result<()> {
163        if self.keyword == "" {
164            return Ok(());
165        }
166
167        let mut formatted_value = self.value.to_string();
168        let remaining_value = if formatted_value.len() > 67 {
169            let remainder = Some(formatted_value[67..].to_string());
170            formatted_value.truncate(67);
171            formatted_value.push_str("&");
172            remainder
173        } else {
174            None
175        };
176
177        let mut card_string = format!("{}'{}'", keyword_string, formatted_value);
178        if let Some(comment) = &self.comment {
179            card_string = format!("{} / {}", card_string, comment);
180        }
181        self.write_formatted_string(writer, card_string, bytes_count)?;
182
183        if let Some(mut remaining_value) = remaining_value {
184            while !remaining_value.is_empty() {
185                let len = remaining_value.len();
186                let take = len.min(67);
187                let continue_card = format!("CONTINUE  '{}&'", &remaining_value[..take]);
188                self.write_formatted_string(writer, continue_card, bytes_count)?;
189
190                remaining_value.drain(..take);
191            }
192        }
193        Ok(())
194    }
195
196    fn write_other_card<W: Write>(
197        &self,
198        writer: &mut W,
199        keyword_string: String,
200        bytes_count: &mut i32,
201    ) -> std::io::Result<()> {
202        // using unwrap_or with an empty string as default
203        if self.keyword == "" {
204            return Ok(());
205        }
206
207        let formatted_value;
208
209        match self.value {
210            CardValue::LOGICAL(_) => {
211                if self.value.as_bool().unwrap() {
212                    formatted_value = format!("{:>20}", "T".to_string());
213                } else {
214                    formatted_value = format!("{:>20}", "F".to_string());
215                }
216            }
217            _ => formatted_value = format!("{:>20}", self.value.to_string()),
218        }
219
220        let mut card_string = format!("{}{}", keyword_string, formatted_value);
221        if let Some(comment) = &self.comment {
222            card_string = format!("{} / {}", card_string, comment);
223        }
224        self.write_formatted_string(writer, card_string, bytes_count)
225    }
226
227    pub fn parse_card(card_str: String) -> Self {
228        if card_str.trim().len() < 1 {
229            return Card::default();
230        }
231
232        let mut keyword;
233        let value;
234        let comment;
235
236        if card_str.starts_with("COMMENT")
237            || card_str.starts_with("HISTORY")
238            || !card_str.contains("=")
239        {
240            let card = Card {
241                keyword: card_str,
242                value: CardValue::EMPTY,
243                comment: None,
244            };
245            return card;
246        }
247        if card_str.starts_with("HIERARCH") {
248            keyword = card_str.splitn(2, '=').collect::<Vec<&str>>()[0].to_string();
249            keyword = keyword.replace("HIERARCH ", "");
250        } else {
251            keyword = card_str.splitn(2, '=').collect::<Vec<&str>>()[0]
252                .trim()
253                .to_string();
254        }
255
256        keyword = keyword.trim_end().to_string();
257
258        let remaining = card_str.splitn(2, '=').collect::<Vec<&str>>()[1].trim();
259        if let Some(idx) = remaining.find(" /") {
260            // If there is a '/' character, we split the remaining string into value and comment.
261            value = remaining[..idx + 1].trim().replace("'", "").to_string();
262            comment = Some(remaining[idx + 2..].trim().to_string());
263        } else {
264            // Otherwise, the whole remaining string is the value.
265            value = remaining.trim().replace("'", "").to_string();
266            comment = None;
267        };
268        // println!("{} {} {:?} {:?}", keyword, value, comment, card_type);
269
270        Card {
271            keyword: keyword,
272            value: check_type(&value),
273            comment: comment,
274        }
275    }
276
277    pub fn continue_card(card: &mut Card, card_str: String) {
278        let mut value;
279        if card_str.starts_with("CONTINUE  ") {
280            value = card_str.splitn(2, "CONTINUE  ").collect::<Vec<&str>>()[1]
281                .trim()
282                .replace("'", "")
283                .to_string();
284            value = value.strip_suffix("&").unwrap_or(&value).to_string();
285
286            let mut last_value = card.get_value_clone();
287            last_value = last_value
288                .strip_suffix("&")
289                .unwrap_or(&last_value)
290                .to_string();
291
292            card.set_value(format!("{}{}", last_value, value));
293        }
294    }
295}