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), comment: comment,
89 }
90 }
91
92 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 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 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 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 value = remaining[..idx + 1].trim().replace("'", "").to_string();
262 comment = Some(remaining[idx + 2..].trim().to_string());
263 } else {
264 value = remaining.trim().replace("'", "").to_string();
266 comment = None;
267 };
268 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}