1use std::collections::HashMap;
6
7use nom::branch::alt;
8use nom::bytes::complete::{tag, take_until, take_while, take_while1};
9use nom::character::complete::{alpha1, digit1, line_ending, multispace0, one_of};
10use nom::combinator::{map, map_res, recognize, rest};
11use nom::error::Error;
12use nom::multi::{fold_many1, many0, many1};
13use nom::number::complete::{double, float};
14use nom::sequence::{delimited, preceded, separated_pair, terminated};
15use nom::{Finish, IResult, Parser};
16use thiserror::Error;
17
18pub mod lsk;
19
20#[derive(Debug, Error, PartialEq)]
21#[error(transparent)]
22pub struct KernelError(#[from] Error<String>);
23
24#[derive(Clone, Debug, PartialEq)]
25enum Value {
26 Double(f64),
27 String(String),
28 Timestamp(String),
29 DoubleArray(Vec<f64>),
30 StringArray(Vec<String>),
31 TimestampArray(Vec<String>),
32}
33
34#[derive(Clone, Debug, PartialEq)]
35pub struct Kernel {
36 type_id: String,
37 items: HashMap<String, Value>,
38}
39
40type Entries = Vec<(String, Value)>;
41
42impl Kernel {
43 pub fn from_string(input: &str) -> Result<Self, KernelError> {
44 let result = kernel(input).map_err(|e| e.to_owned()).finish();
45 match result {
46 Ok((_, (type_id, entries, _))) => Ok(Self {
47 type_id: type_id.to_string(),
48 items: entries.into_iter().collect(),
49 }),
50 Err(err) => Err(KernelError(err)),
51 }
52 }
53
54 pub fn type_id(&self) -> &str {
55 &self.type_id
56 }
57
58 pub fn get_double(&self, key: &str) -> Option<f64> {
59 let value = self.items.get(key)?;
60 if let Value::Double(v) = value {
61 Some(*v)
62 } else {
63 None
64 }
65 }
66
67 pub fn get_double_array(&self, key: &str) -> Option<&Vec<f64>> {
68 let value = self.items.get(key)?;
69 if let Value::DoubleArray(v) = value {
70 Some(v)
71 } else {
72 None
73 }
74 }
75
76 pub fn get_timestamp_array(&self, key: &str) -> Option<&Vec<String>> {
77 let value = self.items.get(key)?;
78 if let Value::TimestampArray(v) = value {
79 Some(v)
80 } else {
81 None
82 }
83 }
84
85 pub fn keys(&self) -> Vec<&String> {
86 self.items.keys().collect()
87 }
88}
89
90fn kernel(s: &str) -> IResult<&str, (&str, Entries, &str)> {
91 let header = preceded(tag("KPL/"), alpha1);
92 let mut parser = (
93 header,
94 fold_many1(
95 preceded(
96 alt((take_until("\\begindata\n"), take_until("\\begindata\r"))),
97 data_block,
98 ),
99 Vec::new,
100 |mut out: Entries, item: Entries| {
101 out.extend(item);
102 out
103 },
104 ),
105 rest,
106 );
107 parser.parse(s)
108}
109
110fn fortran_double(s: &str) -> IResult<&str, f64> {
111 let mut parser = map_res(recognize((double, one_of("dD"), float)), |s: &str| {
112 str::replace(s, ['d', 'D'], "e").parse()
113 });
114 parser.parse(s)
115}
116
117fn spice_double(s: &str) -> IResult<&str, f64> {
118 let mut parser = alt((fortran_double, double));
119 parser.parse(s)
120}
121
122fn spice_string(s: &str) -> IResult<&str, String> {
123 let mut parser = fold_many1(
124 delimited(tag("'"), take_until("'"), tag("'")),
125 String::new,
126 |mut out: String, item: &str| {
127 if !out.is_empty() {
128 out.push('\'');
129 }
130 out.push_str(item);
131 out
132 },
133 );
134 parser.parse(s)
135}
136
137fn timestamp(s: &str) -> IResult<&str, String> {
138 let mut parser = map(
139 alt((
143 preceded(tag("@"), take_while1(|c| !is_separator(c))),
144 digit1,
145 )),
146 String::from,
147 );
148 parser.parse(s)
149}
150
151fn is_separator(c: char) -> bool {
152 c.is_whitespace() || c == ','
153}
154
155fn separator(s: &str) -> IResult<&str, &str> {
156 take_while1(is_separator)(s)
157}
158
159fn double_array(s: &str) -> IResult<&str, Value> {
160 let mut parser = map(
161 delimited(
162 terminated(tag("("), separator),
163 many1(terminated(spice_double, separator)),
164 tag(")"),
165 ),
166 Value::DoubleArray,
167 );
168 parser.parse(s)
169}
170
171fn string_array(s: &str) -> IResult<&str, Value> {
172 let mut parser = map(
173 delimited(
174 terminated(tag("("), separator),
175 many1(terminated(spice_string, separator)),
176 tag(")"),
177 ),
178 Value::StringArray,
179 );
180 parser.parse(s)
181}
182
183fn timestamp_array(s: &str) -> IResult<&str, Value> {
184 let mut parser = map(
185 delimited(
186 terminated(tag("("), separator),
187 many1(terminated(timestamp, separator)),
188 tag(")"),
189 ),
190 Value::TimestampArray,
191 );
192 parser.parse(s)
193}
194
195fn double_value(s: &str) -> IResult<&str, Value> {
196 let mut parser = map(spice_double, Value::Double);
197 parser.parse(s)
198}
199
200fn string_value(s: &str) -> IResult<&str, Value> {
201 let mut parser = map(spice_string, Value::String);
202 parser.parse(s)
203}
204
205fn timestamp_value(s: &str) -> IResult<&str, Value> {
206 let mut parser = map(timestamp, Value::Timestamp);
207 parser.parse(s)
208}
209
210fn array_value(s: &str) -> IResult<&str, Value> {
211 let mut parser = alt((double_array, string_array, timestamp_array));
212 parser.parse(s)
213}
214
215fn key_value(s: &str) -> IResult<&str, (String, Value)> {
216 let mut parser = map(
217 separated_pair(
218 terminated(
219 take_while1(|x: char| !x.is_whitespace() && x != '='),
220 take_while(char::is_whitespace),
221 ),
222 terminated(tag("="), take_while1(char::is_whitespace)),
223 alt((double_value, string_value, timestamp_value, array_value)),
224 ),
225 |kv: (&str, Value)| (kv.0.to_string(), kv.1),
226 );
227 parser.parse(s)
228}
229
230fn start_tag(s: &str) -> IResult<&str, &str> {
231 let mut parser = terminated(tag("\\begindata"), line_ending);
232 parser.parse(s)
233}
234
235fn end_tag(s: &str) -> IResult<&str, &str> {
236 let mut parser = tag("\\begintext");
237 parser.parse(s)
238}
239
240fn data_block(s: &str) -> IResult<&str, Entries> {
241 let mut parser = delimited(
242 start_tag,
243 many0(preceded(multispace0, key_value)),
244 preceded(multispace0, end_tag),
245 );
246 parser.parse(s)
247}
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252
253 #[test]
254 fn parse_err() {
255 let kernel = Kernel::from_string("foo");
256 assert!(kernel.is_err());
257 }
258
259 #[test]
260 fn test_double() {
261 assert_eq!(spice_double("6.3781366e3"), Ok(("", 6378.1366)));
262 assert_eq!(spice_double("+6378.1366"), Ok(("", 6378.1366)));
263 assert_eq!(spice_double("6.3781366D3"), Ok(("", 6378.1366)));
264 assert_eq!(spice_double("6.3781366d3"), Ok(("", 6378.1366)));
265 assert_eq!(spice_double("6.3781366E3"), Ok(("", 6378.1366)));
266 assert_eq!(spice_double("6378"), Ok(("", 6378.0)));
267
268 assert_eq!(
269 double_value("6.3781366e3"),
270 Ok(("", Value::Double(6378.1366)))
271 );
272
273 assert_eq!(spice_double("11e-1"), Ok(("", 1.1)));
274 assert_eq!(spice_double("123E-02"), Ok(("", 1.23)));
275 assert_eq!(spice_double("123K-01"), Ok(("K-01", 123.0)));
276 assert!(spice_double("abc").is_err());
277 }
278
279 #[test]
280 fn test_string() {
281 assert_eq!(
282 spice_string("'KILOMETERS'"),
283 Ok(("", "KILOMETERS".to_string()))
284 );
285 assert_eq!(
286 string_value("'KILOMETERS'"),
287 Ok(("", Value::String("KILOMETERS".to_string())))
288 );
289 assert_eq!(
290 spice_string("'You can''t always get what you want.'"),
291 Ok(("", "You can't always get what you want.".to_string()))
292 );
293 }
294
295 #[test]
296 fn test_timestamp() {
297 assert_eq!(timestamp("@1972-JAN-1"), Ok(("", "1972-JAN-1".to_string())));
298 }
299
300 #[test]
301 fn test_separator() {
302 assert_eq!(separator(" "), Ok(("", " ")));
303 assert_eq!(separator(" , "), Ok(("", " , ")));
304 assert!(separator("foo").is_err());
305 }
306
307 #[test]
308 fn test_double_array() {
309 assert_eq!(
310 double_array("( 6378.1366 6378.1366 6356.7519 )"),
311 Ok((
312 "",
313 Value::DoubleArray(vec!(6378.1366, 6378.1366, 6356.7519))
314 ))
315 );
316 assert_eq!(
317 double_array("( 6378.1366, 6378.1366, 6356.7519 )"),
318 Ok((
319 "",
320 Value::DoubleArray(vec!(6378.1366, 6378.1366, 6356.7519))
321 ))
322 );
323 assert_eq!(
324 double_array("( 2.2031868551400003E+04 )"),
325 Ok(("", Value::DoubleArray(vec!(2.2031868551400003e4))))
326 )
327 }
328
329 #[test]
330 fn test_string_array() {
331 let input = "( 'KILOMETERS','SECONDS' \
332 'KILOMETERS/SECOND' )";
333 assert_eq!(
334 string_array(input),
335 Ok((
336 "",
337 Value::StringArray(vec!(
338 "KILOMETERS".to_string(),
339 "SECONDS".to_string(),
340 "KILOMETERS/SECOND".to_string()
341 ))
342 ))
343 );
344 }
345
346 #[test]
347 fn test_timestamp_array() {
348 let input = "( @1972-JAN-1,@1972-JAN-1 \
349 @1972-JAN-1 )";
350 assert_eq!(
351 timestamp_array(input),
352 Ok((
353 "",
354 Value::TimestampArray(vec!(
355 "1972-JAN-1".to_string(),
356 "1972-JAN-1".to_string(),
357 "1972-JAN-1".to_string()
358 ))
359 ))
360 );
361 }
362
363 #[test]
364 fn test_array() {
365 let exp_float = Value::DoubleArray(vec![6378.1366, 6378.1366, 6356.7519]);
366 let exp_string = Value::StringArray(vec![
367 "KILOMETERS".to_string(),
368 "SECONDS".to_string(),
369 "KILOMETERS/SECOND".to_string(),
370 ]);
371 assert_ne!(Value::Double(3.0), Value::Double(3.1));
372 assert_ne!(exp_float, exp_string);
373 assert_ne!(exp_string, exp_float);
374 assert_eq!(
375 array_value("( 6378.1366, 6378.1366, 6356.7519 )"),
376 Ok(("", exp_float))
377 );
378 let input = "( 'KILOMETERS','SECONDS' \
379 'KILOMETERS/SECOND' )";
380 assert_eq!(array_value(input), Ok(("", exp_string)));
381 }
382
383 #[test]
384 fn test_key_value() {
385 let input = "BODY399_RADII = ( 6378.1366 6378.1366 6356.7519 )";
386 let exp_value = Value::DoubleArray(vec![6378.1366, 6378.1366, 6356.7519]);
387 let exp_key = "BODY399_RADII".to_string();
388 assert_eq!(key_value(input), Ok(("", (exp_key, exp_value))));
389 let input = "BODY1_GM = ( 2.2031868551400003E+04 )";
390 let exp_value = Value::DoubleArray(vec![2.2031868551400003e4]);
391 let exp_key = "BODY1_GM".to_string();
392 assert_eq!(key_value(input), Ok(("", (exp_key, exp_value))));
393 }
394
395 #[test]
396 fn test_data_block() {
397 assert_eq!(start_tag("\\begindata\n"), Ok(("", "\\begindata")));
398 assert!(start_tag("foo \\begindata bar\n").is_err());
399
400 let block = "\\begindata
401
402 BODY499_POLE_RA = ( 317.269202 -0.10927547 0. )
403 BODY499_POLE_DEC = ( 54.432516 -0.05827105 0. )
404 BODY499_PM = ( 176.049863 +350.891982443297 0. )
405
406 BODY499_NUT_PREC_RA = ( 0 0 0 0 0
407 0 0 0 0 0
408 0.000068
409 0.000238
410 0.000052
411 0.000009
412 0.419057 )
413
414
415 BODY499_NUT_PREC_DEC = ( 0 0 0 0 0
416 0 0 0 0 0
417 0 0 0 0 0
418 0.000051
419 0.000141
420 0.000031
421 0.000005
422 1.591274 )
423
424
425 BODY499_NUT_PREC_PM = ( 0 0 0 0 0
426 0 0 0 0 0
427 0 0 0 0 0
428 0 0 0 0 0
429 0.000145
430 0.000157
431 0.000040
432 0.000001
433 0.000001
434 0.584542 )
435
436 \\begintext";
437 let k1 = "BODY499_POLE_RA".to_string();
438 let v1 = Value::DoubleArray(vec![317.269202, -0.10927547, 0.]);
439 let k2 = "BODY499_POLE_DEC".to_string();
440 let v2 = Value::DoubleArray(vec![54.432516, -0.05827105, 0.]);
441 let k3 = "BODY499_PM".to_string();
442 let v3 = Value::DoubleArray(vec![176.049863, 350.891982443297, 0.]);
443 let k4 = "BODY499_NUT_PREC_RA".to_string();
444 let v4 = Value::DoubleArray(vec![
445 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.000068, 0.000238, 0.000052,
446 0.000009, 0.419057,
447 ]);
448 let k5 = "BODY499_NUT_PREC_DEC".to_string();
449 let v5 = Value::DoubleArray(vec![
450 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.000051,
451 0.000141, 0.000031, 0.000005, 1.591274,
452 ]);
453 let k6 = "BODY499_NUT_PREC_PM".to_string();
454 let v6 = Value::DoubleArray(vec![
455 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
456 0.0, 0.0, 0.0, 0.000145, 0.000157, 0.000040, 0.000001, 0.000001, 0.584542,
457 ]);
458 let exp = vec![(k1, v1), (k2, v2), (k3, v3), (k4, v4), (k5, v5), (k6, v6)];
459 assert_eq!(data_block(block), Ok(("", exp)));
460 }
461}