1use crate::common::{AdmHeader, OdmHeader, OpmCovarianceMatrix, SpacecraftParameters, StateVector};
22use crate::error::{
23 CcsdsNdmError, EnumParseError, FormatError, InternalParserError, ValidationError,
24};
25use crate::traits::{CcsdsNullable, FromKvnFloat, FromKvnValue};
26use crate::types::{UserDefined, UserDefinedParameter, *};
27use fast_float;
28use std::str::FromStr;
29use winnow::ascii::{line_ending, space0, till_line_ending};
30use winnow::combinator::{alt, delimited, opt, peek, preceded, repeat, terminated};
31use winnow::error::{
32 AddContext, ErrMode, FromExternalError, ParserError, StrContext, StrContextValue,
33};
34use winnow::prelude::*;
35use winnow::stream::Offset;
36use winnow::token::{one_of, take_till, take_while};
37
38pub type KvnResult<O, E = InternalParserError> = Result<O, ErrMode<E>>;
40
41pub fn parse_f64_winnow(input: &mut &str) -> KvnResult<f64> {
47 let s = take_while::<_, _, ()>(1.., ('0'..='9', '.', '-', '+', 'e', 'E'))
48 .parse_next(input)
49 .map_err(|_| cut_err(input, "Invalid float"))?;
50 fast_float::parse(s).map_err(|_| cut_err(input, "Invalid float"))
51}
52
53pub fn till_space<'a>(input: &mut &'a str) -> KvnResult<&'a str> {
55 preceded(ws, take_till(1.., (' ', '\t', '\r', '\n'))).parse_next(input)
56}
57
58pub fn till_space_or_eol<'a>(input: &mut &'a str) -> KvnResult<&'a str> {
60 preceded(ws, take_till(1.., (' ', '\t', '\r', '\n'))).parse_next(input)
61}
62
63pub fn kv_float_unit_opt<'a>(input: &mut &'a str) -> KvnResult<(Option<f64>, Option<&'a str>)> {
66 ws.parse_next(input)?;
67
68 if peek(parse_f64_winnow).parse_next(input).is_ok() {
71 let f = parse_f64_winnow.parse_next(input)?;
72 let u = kv_unit.parse_next(input)?;
73 opt_line_ending.parse_next(input)?;
74 Ok((Some(f), u))
75 } else {
76 let u = kv_unit.parse_next(input)?;
78
79 let remainder = till_line_ending.parse_next(input)?;
82 if !remainder.is_null() {
83 return Err(cut_err(input, "Invalid float value"));
84 }
85
86 opt_line_ending.parse_next(input)?;
87 Ok((None, u))
88 }
89}
90
91pub fn to_ccsds_error(
97 input: &str,
98 err: winnow::error::ParseError<&str, InternalParserError>,
99) -> CcsdsNdmError {
100 let offset = err.offset();
101 let inner = err.into_inner();
102
103 let base_err = match *inner.kind {
104 crate::error::ParserErrorKind::Validation(e) => CcsdsNdmError::Validation(Box::new(e)),
105 crate::error::ParserErrorKind::Epoch(e) => CcsdsNdmError::Epoch(e),
106 crate::error::ParserErrorKind::Enum(e) => {
107 CcsdsNdmError::Format(Box::new(FormatError::Enum(e)))
108 }
109 crate::error::ParserErrorKind::ParseInt(e) => {
110 CcsdsNdmError::Format(Box::new(FormatError::ParseInt(e)))
111 }
112 crate::error::ParserErrorKind::ParseFloat(e) => {
113 CcsdsNdmError::Format(Box::new(FormatError::ParseFloat(e)))
114 }
115 crate::error::ParserErrorKind::MissingRequiredField { block, field } => {
116 return CcsdsNdmError::Validation(Box::new(ValidationError::MissingRequiredField {
117 block: std::borrow::Cow::Borrowed(block),
118 field: std::borrow::Cow::Borrowed(field),
119 line: None,
120 }))
121 .with_location(input, offset);
122 }
123 _ => {
124 let message = inner.message;
125
126 let raw = crate::error::RawParsePosition {
127 offset,
128 message,
129 contexts: inner.contexts,
130 };
131 CcsdsNdmError::Format(Box::new(FormatError::Kvn(Box::new(
132 raw.into_parse_error(input),
133 ))))
134 }
135 };
136
137 base_err.with_location(input, offset)
138}
139
140pub fn cut_err(input: &mut &str, label: &'static str) -> ErrMode<InternalParserError> {
142 ErrMode::Cut(InternalParserError::from_input(input).add_context(
143 input,
144 &input.checkpoint(),
145 StrContext::Label(label),
146 ))
147}
148
149pub fn missing_field_err(
151 _input: &mut &str,
152 block: &'static str,
153 field: &'static str,
154) -> ErrMode<InternalParserError> {
155 ErrMode::Cut(InternalParserError {
156 message: std::borrow::Cow::Borrowed(""),
157 contexts: crate::error::ContextStack::new(),
158 kind: Box::new(crate::error::ParserErrorKind::MissingRequiredField { block, field }),
159 })
160}
161
162pub fn require_field<T>(
164 input: &mut &str,
165 block: &'static str,
166 field: &'static str,
167 value: Option<T>,
168) -> KvnResult<T> {
169 value.ok_or_else(|| missing_field_err(input, block, field))
170}
171
172pub fn ws<'a>(input: &mut &'a str) -> KvnResult<&'a str> {
178 space0.parse_next(input)
179}
180
181pub fn keyword<'a>(input: &mut &'a str) -> KvnResult<&'a str> {
184 (
185 one_of('A'..='Z'),
186 take_while(0.., ('A'..='Z', '0'..='9', '_')),
187 )
188 .take()
189 .parse_next(input)
190}
191
192pub fn kv_sep(input: &mut &str) -> KvnResult<()> {
194 (ws, '=', ws).void().parse_next(input)
195}
196
197pub fn kv_unit<'a>(input: &mut &'a str) -> KvnResult<Option<&'a str>> {
200 ws.parse_next(input)?;
201 if input.starts_with('[') {
202 let u = delimited('[', take_till(0.., |c: char| c == ']'), ']')
203 .context(StrContext::Label("unit in brackets"))
204 .parse_next(input)?;
205 Ok(Some(u))
206 } else {
207 Ok(None)
208 }
209}
210
211pub fn kvn_value<'a>(input: &mut &'a str) -> KvnResult<(&'a str, Option<&'a str>)> {
214 let val = take_till(0.., |c: char| c == '[' || c == '\r' || c == '\n')
215 .map(|s: &str| s.trim())
216 .parse_next(input)?;
217
218 if val.is_empty() {
219 let rest = till_line_ending.parse_next(input)?;
224 let trimmed = rest.trim();
225 Ok((trimmed, None))
226 } else {
227 let unit = kv_unit.parse_next(input)?;
228 Ok((val, unit))
229 }
230}
231
232#[derive(Debug, Clone, PartialEq)]
238pub enum KvnToken<'a> {
239 KeyValue {
241 key: &'a str,
242 value: &'a str,
243 unit: Option<&'a str>,
244 },
245 Comment(&'a str),
247 BlockStart(&'a str),
249 BlockEnd(&'a str),
251 Raw(&'a str),
253 Empty,
255}
256
257pub fn comment_line<'a>(input: &mut &'a str) -> KvnResult<&'a str> {
259 preceded((ws, "COMMENT", space0), till_line_ending).parse_next(input)
260}
261
262pub fn key_value_line<'a>(input: &mut &'a str) -> KvnResult<(&'a str, &'a str, Option<&'a str>)> {
264 (preceded(ws, keyword), kv_sep, kvn_value)
265 .map(|(key, _, (value, unit))| (key, value, unit))
266 .parse_next(input)
267}
268
269pub fn block_start<'a>(input: &mut &'a str) -> KvnResult<&'a str> {
271 let content = preceded(ws, till_line_ending).parse_next(input)?;
272 let content = content.trim();
273
274 if let Some(prefix) = content.strip_suffix("_START") {
275 if !prefix.contains(char::is_whitespace) {
276 return Ok(prefix);
277 }
278 }
279 Err(ErrMode::Backtrack(InternalParserError::from_input(input)))
280}
281
282pub fn block_end<'a>(input: &mut &'a str) -> KvnResult<&'a str> {
284 let content = preceded(ws, till_line_ending).parse_next(input)?;
285 let content = content.trim();
286
287 if let Some(prefix) = content.strip_suffix("_STOP") {
288 if !prefix.contains(char::is_whitespace) {
289 return Ok(prefix);
290 }
291 } else if let Some(prefix) = content.strip_suffix("_END") {
292 if !prefix.contains(char::is_whitespace) {
293 return Ok(prefix);
294 }
295 }
296 Err(ErrMode::Backtrack(InternalParserError::from_input(input)))
297}
298
299pub fn empty_line(input: &mut &str) -> KvnResult<()> {
301 (
302 ws,
303 peek(alt((line_ending.void(), winnow::combinator::eof.void()))),
304 )
305 .void()
306 .parse_next(input)
307}
308
309pub fn raw_line<'a>(input: &mut &'a str) -> KvnResult<&'a str> {
311 let content = preceded(ws, till_line_ending).parse_next(input)?;
312 let trimmed = content.trim();
313
314 if trimmed.is_empty()
316 || trimmed.starts_with("COMMENT")
317 || trimmed.contains('=')
318 || trimmed.ends_with("_START")
319 || trimmed.ends_with("_STOP")
320 || trimmed.ends_with("_END")
321 {
322 return Err(ErrMode::Backtrack(InternalParserError::from_input(input)));
323 }
324
325 Ok(trimmed)
326}
327
328pub fn kvn_token<'a>(input: &mut &'a str) -> KvnResult<KvnToken<'a>> {
330 ws.parse_next(input)?;
332
333 alt((
334 empty_line.map(|_| KvnToken::Empty),
335 comment_line.map(KvnToken::Comment),
336 block_start.map(KvnToken::BlockStart),
337 block_end.map(KvnToken::BlockEnd),
338 key_value_line.map(|(k, v, u)| KvnToken::KeyValue {
339 key: k,
340 value: v,
341 unit: u,
342 }),
343 raw_line.map(KvnToken::Raw),
344 ))
345 .parse_next(input)
346}
347
348pub fn key_token<'a>(input: &mut &'a str) -> KvnResult<&'a str> {
350 terminated(preceded(ws, keyword), kv_sep).parse_next(input)
351}
352
353pub fn kv_rest<'a>(input: &mut &'a str) -> KvnResult<(&'a str, Option<&'a str>)> {
355 terminated(kvn_value, opt_line_ending).parse_next(input)
356}
357
358pub fn kv_float(input: &mut &str) -> KvnResult<f64> {
360 let checkpoint = input.checkpoint();
361 terminated(
362 (
363 parse_f64_winnow.context(StrContext::Label("float")),
364 kv_unit,
365 )
366 .map(|(f, _)| f),
367 opt_line_ending,
368 )
369 .parse_next(input)
370 .map_err(|e| {
371 if e.is_backtrack() {
372 let mut err = InternalParserError::from_input(input);
373 err.message = std::borrow::Cow::Borrowed("Invalid float");
374 ErrMode::Cut(err.add_context(input, &checkpoint, StrContext::Label("Invalid float")))
375 } else {
376 e
377 }
378 })
379}
380
381pub fn kv_i32(input: &mut &str) -> KvnResult<i32> {
383 let checkpoint = input.checkpoint();
384 terminated(
385 (
386 take_while(1.., ('0'..='9', '-', '+'))
387 .map(|s: &str| s.parse::<i32>())
388 .verify(|res| res.is_ok())
389 .map(|res| res.unwrap()),
390 kv_unit,
391 )
392 .map(|(i, _)| i),
393 opt_line_ending,
394 )
395 .parse_next(input)
396 .map_err(|e| {
397 if e.is_backtrack() {
398 let mut err = InternalParserError::from_input(input);
399 err.message = std::borrow::Cow::Borrowed("Invalid integer");
400 ErrMode::Cut(err.add_context(input, &checkpoint, StrContext::Label("Invalid integer")))
401 } else {
402 e
403 }
404 })
405}
406
407pub fn kv_u32(input: &mut &str) -> KvnResult<u32> {
409 let checkpoint = input.checkpoint();
410 terminated(
411 (
412 take_while(1.., '0'..='9')
413 .map(|s: &str| s.parse::<u32>())
414 .verify(|res| res.is_ok())
415 .map(|res| res.unwrap()),
416 kv_unit,
417 )
418 .map(|(u, _)| u),
419 opt_line_ending,
420 )
421 .parse_next(input)
422 .map_err(|e| {
423 if e.is_backtrack() {
424 let mut err = InternalParserError::from_input(input);
425 err.message = std::borrow::Cow::Borrowed("Invalid unsigned integer");
426 ErrMode::Cut(err.add_context(
427 input,
428 &checkpoint,
429 StrContext::Label("Invalid unsigned integer"),
430 ))
431 } else {
432 e
433 }
434 })
435}
436
437pub fn kv_u32_opt(input: &mut &str) -> KvnResult<Option<u32>> {
439 let checkpoint = input.checkpoint();
440 ws.parse_next(input)?;
441
442 let remainder = peek(till_line_ending).parse_next(input)?;
444 if remainder.is_null() || remainder.trim().starts_with('[') {
445 let _ = kv_unit.parse_next(input)?;
446 opt_line_ending.parse_next(input)?;
447 return Ok(None);
448 }
449
450 terminated(
451 (
452 take_while(1.., '0'..='9')
453 .map(|s: &str| s.parse::<u32>())
454 .verify(|res| res.is_ok())
455 .map(|res| res.ok()),
456 kv_unit,
457 )
458 .map(|(u, _)| u),
459 opt_line_ending,
460 )
461 .parse_next(input)
462 .map_err(|e| {
463 if e.is_backtrack() {
464 let mut err = InternalParserError::from_input(input);
465 err.message = std::borrow::Cow::Borrowed("Invalid unsigned integer");
466 ErrMode::Cut(err.add_context(
467 input,
468 &checkpoint,
469 StrContext::Label("Invalid unsigned integer"),
470 ))
471 } else {
472 e
473 }
474 })
475}
476
477pub fn kv_u64(input: &mut &str) -> KvnResult<u64> {
479 let checkpoint = input.checkpoint();
480 terminated(
481 (
482 take_while(1.., '0'..='9')
483 .map(|s: &str| s.parse::<u64>())
484 .verify(|res| res.is_ok())
485 .map(|res| res.unwrap()),
486 kv_unit,
487 )
488 .map(|(u, _)| u),
489 opt_line_ending,
490 )
491 .parse_next(input)
492 .map_err(|e| {
493 if e.is_backtrack() {
494 let mut err = InternalParserError::from_input(input);
495 err.message = std::borrow::Cow::Borrowed("Invalid unsigned integer");
496 ErrMode::Cut(err.add_context(
497 input,
498 &checkpoint,
499 StrContext::Label("Invalid unsigned integer"),
500 ))
501 } else {
502 e
503 }
504 })
505}
506
507pub fn kv_u64_opt(input: &mut &str) -> KvnResult<Option<u64>> {
509 let checkpoint = input.checkpoint();
510 ws.parse_next(input)?;
511
512 let remainder = peek(till_line_ending).parse_next(input)?;
514 if remainder.is_null() || remainder.trim().starts_with('[') {
515 let _ = kv_unit.parse_next(input)?;
516 opt_line_ending.parse_next(input)?;
517 return Ok(None);
518 }
519
520 terminated(
521 (
522 take_while(1.., '0'..='9')
523 .map(|s: &str| s.parse::<u64>())
524 .verify(|res| res.is_ok())
525 .map(|res| res.ok()),
526 kv_unit,
527 )
528 .map(|(u, _)| u),
529 opt_line_ending,
530 )
531 .parse_next(input)
532 .map_err(|e| {
533 if e.is_backtrack() {
534 let mut err = InternalParserError::from_input(input);
535 err.message = std::borrow::Cow::Borrowed("Invalid unsigned integer");
536 ErrMode::Cut(err.add_context(
537 input,
538 &checkpoint,
539 StrContext::Label("Invalid unsigned integer"),
540 ))
541 } else {
542 e
543 }
544 })
545}
546
547pub fn skip_empty_lines(input: &mut &str) -> KvnResult<()> {
549 repeat(0.., (space0, line_ending))
550 .map(|_: ()| ()) .parse_next(input)
552}
553
554pub fn opt_line_ending(input: &mut &str) -> KvnResult<()> {
556 (space0, opt(line_ending)).void().parse_next(input)
557}
558
559pub fn kv_string(input: &mut &str) -> KvnResult<String> {
565 let v = terminated(till_line_ending, opt_line_ending).parse_next(input)?;
566 Ok(v.trim().to_string())
567}
568
569pub fn kv_string_opt(input: &mut &str) -> KvnResult<Option<String>> {
572 let v = terminated(till_line_ending, opt_line_ending).parse_next(input)?;
573 let trimmed = v.trim();
574 if trimmed.is_null() {
575 Ok(None)
576 } else {
577 Ok(Some(trimmed.to_string()))
578 }
579}
580
581pub fn kv_epoch(input: &mut &str) -> KvnResult<Epoch> {
583 let v = terminated(till_line_ending, opt_line_ending).parse_next(input)?;
584 Epoch::from_str(v.trim())
585 .map_err(|e| ErrMode::Cut(InternalParserError::from_external_error(input, e)))
586}
587
588pub fn kv_epoch_opt(input: &mut &str) -> KvnResult<Option<Epoch>> {
590 let v = terminated(till_line_ending, opt_line_ending).parse_next(input)?;
591 let trimmed = v.trim();
592 if trimmed.is_null() {
593 Ok(None)
594 } else {
595 Epoch::from_str(trimmed)
596 .map(Some)
597 .map_err(|e| ErrMode::Cut(InternalParserError::from_external_error(input, e)))
598 }
599}
600
601pub fn kv_epoch_token(input: &mut &str) -> KvnResult<Epoch> {
603 let v = till_space.parse_next(input)?;
604 Epoch::from_str(v.trim())
605 .map_err(|e| ErrMode::Cut(InternalParserError::from_external_error(input, e)))
606}
607
608pub fn kv_yes_no(input: &mut &str) -> KvnResult<YesNo> {
610 let v = terminated(till_line_ending, opt_line_ending).parse_next(input)?;
611 YesNo::from_str(v.trim())
612 .map_err(|e| ErrMode::Cut(InternalParserError::from_external_error(input, e)))
613}
614
615pub fn kv_yes_no_opt(input: &mut &str) -> KvnResult<Option<YesNo>> {
617 let v = terminated(till_line_ending, opt_line_ending).parse_next(input)?;
618 let trimmed = v.trim();
619 if trimmed.is_null() {
620 Ok(None)
621 } else {
622 YesNo::from_str(trimmed)
623 .map(Some)
624 .map_err(|e| ErrMode::Cut(InternalParserError::from_external_error(input, e)))
625 }
626}
627
628pub fn kv_enum<T: FromStr>(input: &mut &str) -> KvnResult<T>
630where
631 EnumParseError: From<T::Err>,
632{
633 let v = terminated(till_line_ending, opt_line_ending).parse_next(input)?;
634 T::from_str(v.trim()).map_err(|e| {
635 ErrMode::Cut(InternalParserError::from_external_error(
636 input,
637 EnumParseError::from(e),
638 ))
639 })
640}
641
642pub fn kv_enum_opt<T: FromStr>(input: &mut &str) -> KvnResult<Option<T>>
644where
645 EnumParseError: From<T::Err>,
646{
647 let v = terminated(till_line_ending, opt_line_ending).parse_next(input)?;
648 let trimmed = v.trim();
649 if trimmed.is_null() {
650 Ok(None)
651 } else {
652 T::from_str(trimmed).map(Some).map_err(|e| {
653 ErrMode::Cut(InternalParserError::from_external_error(
654 input,
655 EnumParseError::from(e),
656 ))
657 })
658 }
659}
660
661pub fn kv_from_kvn_value<T: FromKvnValue>(input: &mut &str) -> KvnResult<T> {
663 let (v, _) = kv_rest.parse_next(input)?;
664 T::from_kvn_value(v)
665 .map_err(|e| ErrMode::Cut(InternalParserError::from_external_error(input, e)))
666}
667
668pub fn kv_from_kvn<T: FromKvnFloat>(input: &mut &str) -> KvnResult<T> {
670 let (v, u) = kv_float_unit.parse_next(input)?;
671 T::from_kvn_float(v, u)
672 .map_err(|e| ErrMode::Cut(InternalParserError::from_external_error(input, e)))
673}
674
675pub fn kv_from_kvn_opt<T: FromKvnFloat>(input: &mut &str) -> KvnResult<Option<T>> {
677 let (v, u) = kv_float_unit_opt.parse_next(input)?;
678 if let Some(val) = v {
679 T::from_kvn_float(val, u)
680 .map(Some)
681 .map_err(|e| ErrMode::Cut(InternalParserError::from_external_error(input, e)))
682 } else {
683 Ok(None)
684 }
685}
686
687pub fn kv_float_unit<'a>(input: &mut &'a str) -> KvnResult<(f64, Option<&'a str>)> {
689 terminated((parse_f64_winnow, kv_unit), opt_line_ending).parse_next(input)
690}
691
692pub fn parse_f64(value: &str) -> crate::error::Result<f64> {
698 value.trim().parse::<f64>().map_err(CcsdsNdmError::from)
699}
700
701pub fn parse_i32(value: &str) -> crate::error::Result<i32> {
703 value.trim().parse::<i32>().map_err(CcsdsNdmError::from)
704}
705
706pub fn parse_u32(value: &str) -> crate::error::Result<u32> {
708 value.trim().parse::<u32>().map_err(CcsdsNdmError::from)
709}
710
711pub fn parse_u64(value: &str) -> crate::error::Result<u64> {
713 value.trim().parse::<u64>().map_err(CcsdsNdmError::from)
714}
715
716pub trait ParseKvn: Sized {
725 fn parse_kvn(input: &mut &str) -> KvnResult<Self>;
727
728 fn from_kvn_str(s: &str) -> crate::error::Result<Self> {
730 kvn_entry(Self::parse_kvn)
731 .parse(s)
732 .map_err(|e| to_ccsds_error(s, e))
733 }
734}
735
736pub fn expect_kv<'a, T, P>(
743 expected_key: &'static str,
744 mut val_parser: P,
745) -> impl FnMut(&mut &'a str) -> KvnResult<(T, Option<&'a str>)>
746where
747 P: winnow::Parser<&'a str, T, ErrMode<InternalParserError>>,
748{
749 move |input: &mut &'a str| {
750 (
751 ws,
752 keyword.context(StrContext::Label("KVN keyword")),
753 kv_sep,
754 )
755 .verify(|(_, key, _)| *key == expected_key)
756 .context(StrContext::Expected(StrContextValue::Description(
757 expected_key,
758 )))
759 .parse_next(input)?;
760
761 let val = val_parser.parse_next(input)?;
762 let unit = kv_unit.parse_next(input)?;
763 opt_line_ending.parse_next(input)?;
764
765 Ok((val, unit))
766 }
767}
768
769pub fn expect_key<'a>(
772 expected_key: &'static str,
773) -> impl FnMut(&mut &'a str) -> KvnResult<(&'a str, Option<&'a str>)> {
774 expect_kv(expected_key, kvn_value_only)
775}
776
777fn kvn_value_only<'a>(input: &mut &'a str) -> KvnResult<&'a str> {
778 take_till(0.., |c: char| c == '[' || c == '\r' || c == '\n')
779 .map(|s: &str| s.trim())
780 .parse_next(input)
781}
782
783pub fn key_matching<'a, F>(
786 predicate: F,
787) -> impl FnMut(&mut &'a str) -> KvnResult<(&'a str, &'a str, Option<&'a str>)>
788where
789 F: Fn(&str) -> bool + Copy,
790{
791 move |input: &mut &'a str| {
792 ws.parse_next(input)?;
793 let key = keyword.parse_next(input)?;
794 if !predicate(key) {
795 return Err(ErrMode::Backtrack(InternalParserError::from_input(input)));
796 }
797 kv_sep.parse_next(input)?;
798 let (value, unit) = kvn_value.parse_next(input)?;
799 opt_line_ending.parse_next(input)?;
800 Ok((key, value, unit))
801 }
802}
803
804pub fn collect_comments(input: &mut &str) -> KvnResult<Vec<String>> {
806 let checkpoint = input.checkpoint();
808 let _ = ws.parse_next(input)?;
809 if input.is_empty()
810 || (!input.starts_with("COMMENT") && !input.starts_with('\r') && !input.starts_with('\n'))
811 {
812 input.reset(&checkpoint);
813 return Ok(Vec::new());
814 }
815 input.reset(&checkpoint);
816
817 repeat(
818 0..,
819 alt((
820 preceded(ws, comment_line).map(|s| Some(s.trim().to_string())),
822 (ws, line_ending).map(|_| None),
823 )),
824 )
825 .fold(Vec::new, |mut acc: Vec<String>, item| {
826 if let Some(s) = item {
827 acc.push(s);
828 }
829 acc
830 })
831 .parse_next(input)
832}
833
834pub fn skip_empty_and_comments(input: &mut &str) -> KvnResult<()> {
836 loop {
837 let checkpoint = input.checkpoint();
838 if alt(((ws, comment_line).void(), (ws, line_ending).void()))
839 .parse_next(input)
840 .is_err()
841 {
842 input.reset(&checkpoint);
843 break;
844 }
845 }
846 let _ = ws.parse_next(input);
847 Ok(())
848}
849
850pub fn kvn_entry<'a, O, P>(mut parser: P) -> impl FnMut(&mut &'a str) -> KvnResult<O>
852where
853 P: Parser<&'a str, O, ErrMode<InternalParserError>>,
854{
855 move |input: &mut &'a str| {
856 skip_empty_and_comments.parse_next(input)?;
857 let res = parser.parse_next(input)?;
858 let _ = skip_empty_and_comments.parse_next(input);
859 Ok(res)
860 }
861}
862
863#[macro_export]
868macro_rules! parse_block {
869 ($input:ident, $comments:expr, {
871 $($($key:literal)|+ => $target:ident : $parser:expr $(=> $action:block)? ),* $(,)?
872 }, $break_condition:expr $(, $error_label:expr)?)
873 => {
874 loop {
875 let checkpoint = $input.checkpoint();
876 let loop_comments = collect_comments.parse_next($input)?;
877
878 if ($break_condition)(&mut *$input) {
879 $comments.extend(loop_comments);
880 break;
881 }
882
883 let key = match key_token.parse_next($input) {
884 Ok(k) => k,
885 Err(_) => {
886 $input.reset(&checkpoint);
887 break;
888 }
889 };
890
891 match key {
892 $( $($key)|+ => {
894 let val = $parser.parse_next($input)?;
895 parse_block!(@action $comments, loop_comments, val, $target $(, $action)?);
896 }
897 )*
898 _ => {
899 $input.reset(&checkpoint);
900 $( return Err(winnow::error::ErrMode::Cut(InternalParserError::from_input($input).add_context(
902 $input,
903 &$input.checkpoint(),
904 winnow::error::StrContext::Label($error_label),
905 )));
906 )?
907 #[allow(unreachable_code)]
908 {
909 break;
910 }
911 }
912 }
913 }
914 };
915
916 (@action $comments:expr, $loop_comments:ident, $val:ident, $target:ident) => {
918 $comments.extend($loop_comments);
919 $target = Some($val);
920 };
921 (@action $comments:expr, $loop_comments:ident, $val:ident, $binding:ident, $action:block) => {
922 $comments.extend($loop_comments);
923 let $binding = $val;
924 $action
925 };
926}
927
928pub fn at_block_start(tag: &str, input: &str) -> bool {
930 let s = input.trim_start_matches([' ', '\t']);
931 if let Some(rest) = s.strip_prefix(tag) {
932 if let Some(suffix) = rest.strip_prefix("_START") {
933 return suffix.starts_with('\r') || suffix.starts_with('\n') || suffix.is_empty();
934 }
935 }
936 false
937}
938
939pub fn at_block_end(tag: &str, input: &str) -> bool {
941 let s = input.trim_start_matches([' ', '\t']);
942 if let Some(rest) = s.strip_prefix(tag) {
943 if let Some(suffix) = rest
944 .strip_prefix("_STOP")
945 .or_else(|| rest.strip_prefix("_END"))
946 {
947 return suffix.starts_with('\r') || suffix.starts_with('\n') || suffix.is_empty();
948 }
949 }
950 false
951}
952
953pub fn expect_block_start<'a>(
955 expected_tag: &'static str,
956) -> impl FnMut(&mut &'a str) -> KvnResult<()> {
957 move |input: &mut &'a str| {
958 (ws, block_start, opt_line_ending)
959 .verify(|(_, tag, _)| *tag == expected_tag)
960 .void()
961 .context(StrContext::Label("Block start"))
962 .context(StrContext::Expected(StrContextValue::Description(
963 expected_tag,
964 )))
965 .parse_next(input)
966 }
967}
968
969pub fn expect_block_end<'a>(
971 expected_tag: &'static str,
972) -> impl FnMut(&mut &'a str) -> KvnResult<()> {
973 move |input: &mut &'a str| {
974 (ws, block_end, opt_line_ending)
975 .verify(|(_, tag, _)| *tag == expected_tag)
976 .void()
977 .context(StrContext::Label("Block end"))
978 .context(StrContext::Expected(StrContextValue::Description(
979 expected_tag,
980 )))
981 .parse_next(input)
982 }
983}
984
985pub fn odm_header(input: &mut &str) -> KvnResult<OdmHeader> {
987 let mut comment = Vec::new();
988 let mut classification = None;
989 let mut creation_date = None;
990 let mut originator = None;
991 let mut message_id = None;
992
993 loop {
994 let checkpoint = input.checkpoint();
995 comment.extend(collect_comments.parse_next(input)?);
996
997 let key = match preceded(ws, keyword).parse_next(input) {
998 Ok(k) => k,
999 Err(_) => {
1000 input.reset(&checkpoint);
1001 break;
1002 }
1003 };
1004
1005 if key == "OBJECT_NAME" || key == "META_START" {
1007 input.reset(&checkpoint);
1008 break;
1009 }
1010
1011 kv_sep.parse_next(input)?;
1012 match key {
1013 "CLASSIFICATION" => {
1014 classification = Some(kv_string.parse_next(input)?);
1015 }
1016 "CREATION_DATE" => {
1017 creation_date = Some(kv_epoch.parse_next(input)?);
1018 }
1019 "ORIGINATOR" => {
1020 originator = Some(kv_string.parse_next(input)?);
1021 }
1022 "MESSAGE_ID" => {
1023 message_id = Some(kv_string.parse_next(input)?);
1024 }
1025 _ => {
1026 input.reset(&checkpoint);
1027 break;
1028 }
1029 }
1030
1031 if input.offset_from(&checkpoint) == 0 {
1032 break;
1033 }
1034 }
1035
1036 Ok(OdmHeader {
1037 comment,
1038 classification,
1039 creation_date: creation_date.ok_or_else(|| cut_err(input, "Expected CREATION_DATE"))?,
1040 originator: originator.ok_or_else(|| cut_err(input, "Expected ORIGINATOR"))?,
1041 message_id,
1042 })
1043}
1044
1045pub fn adm_header(input: &mut &str) -> KvnResult<AdmHeader> {
1047 let mut comment = Vec::new();
1048 let mut classification = None;
1049 let mut creation_date = None;
1050 let mut originator = None;
1051 let mut message_id = None;
1052
1053 loop {
1054 let checkpoint = input.checkpoint();
1055 comment.extend(collect_comments.parse_next(input)?);
1056
1057 let key = match preceded(ws, keyword).parse_next(input) {
1058 Ok(k) => k,
1059 Err(_) => {
1060 input.reset(&checkpoint);
1061 break;
1062 }
1063 };
1064
1065 if key == "OBJECT_NAME" || key == "META_START" {
1068 input.reset(&checkpoint);
1069 break;
1070 }
1071
1072 kv_sep.parse_next(input)?;
1073 match key {
1074 "CLASSIFICATION" => {
1075 classification = Some(kv_string.parse_next(input)?);
1076 }
1077 "CREATION_DATE" => {
1078 creation_date = Some(kv_epoch.parse_next(input)?);
1079 }
1080 "ORIGINATOR" => {
1081 originator = Some(kv_string.parse_next(input)?);
1082 }
1083 "MESSAGE_ID" => {
1084 message_id = Some(kv_string.parse_next(input)?);
1085 }
1086 _ => {
1087 input.reset(&checkpoint);
1088 break;
1089 }
1090 }
1091
1092 if input.offset_from(&checkpoint) == 0 {
1093 break;
1094 }
1095 }
1096
1097 Ok(AdmHeader {
1098 comment,
1099 classification,
1100 creation_date: creation_date.ok_or_else(|| cut_err(input, "Expected CREATION_DATE"))?,
1101 originator: originator.ok_or_else(|| cut_err(input, "Expected ORIGINATOR"))?,
1102 message_id,
1103 })
1104}
1105
1106pub fn state_vector(input: &mut &str) -> KvnResult<(Vec<String>, StateVector)> {
1112 let mut comment = Vec::new();
1113 let mut epoch = None;
1114 let mut x = None;
1115 let mut y = None;
1116 let mut z = None;
1117 let mut x_dot = None;
1118 let mut y_dot = None;
1119 let mut z_dot = None;
1120
1121 parse_block!(input, comment, {
1122 "EPOCH" => epoch: kv_epoch,
1123 "X" => x: kv_from_kvn,
1124 "Y" => y: kv_from_kvn,
1125 "Z" => z: kv_from_kvn,
1126 "X_DOT" => x_dot: kv_from_kvn,
1127 "Y_DOT" => y_dot: kv_from_kvn,
1128 "Z_DOT" => z_dot: kv_from_kvn,
1129 }, |_| false);
1130
1131 let sv = StateVector {
1132 comment: Vec::new(), epoch: epoch.ok_or_else(|| missing_field_err(input, "State Vector", "EPOCH"))?,
1134 x: x.ok_or_else(|| missing_field_err(input, "State Vector", "X"))?,
1135 y: y.ok_or_else(|| missing_field_err(input, "State Vector", "Y"))?,
1136 z: z.ok_or_else(|| missing_field_err(input, "State Vector", "Z"))?,
1137 x_dot: x_dot.ok_or_else(|| missing_field_err(input, "State Vector", "X_DOT"))?,
1138 y_dot: y_dot.ok_or_else(|| missing_field_err(input, "State Vector", "Y_DOT"))?,
1139 z_dot: z_dot.ok_or_else(|| missing_field_err(input, "State Vector", "Z_DOT"))?,
1140 };
1141
1142 Ok((comment, sv))
1143}
1144
1145pub fn covariance_matrix(input: &mut &str) -> KvnResult<Option<OpmCovarianceMatrix>> {
1147 let mut comment = Vec::new();
1148 let mut cov_ref_frame = None;
1149 let mut cx_x = None;
1150 let mut cy_x = None;
1151 let mut cy_y = None;
1152 let mut cz_x = None;
1153 let mut cz_y = None;
1154 let mut cz_z = None;
1155 let mut cx_dot_x = None;
1156 let mut cx_dot_y = None;
1157 let mut cx_dot_z = None;
1158 let mut cx_dot_x_dot = None;
1159 let mut cy_dot_x = None;
1160 let mut cy_dot_y = None;
1161 let mut cy_dot_z = None;
1162 let mut cy_dot_x_dot = None;
1163 let mut cy_dot_y_dot = None;
1164 let mut cz_dot_x = None;
1165 let mut cz_dot_y = None;
1166 let mut cz_dot_z = None;
1167 let mut cz_dot_x_dot = None;
1168 let mut cz_dot_y_dot = None;
1169 let mut cz_dot_z_dot = None;
1170
1171 parse_block!(input, comment, {
1172 "COV_REF_FRAME" => cov_ref_frame: kv_string,
1173 "CX_X" => cx_x: kv_from_kvn,
1174 "CY_X" => cy_x: kv_from_kvn,
1175 "CY_Y" => cy_y: kv_from_kvn,
1176 "CZ_X" => cz_x: kv_from_kvn,
1177 "CZ_Y" => cz_y: kv_from_kvn,
1178 "CZ_Z" => cz_z: kv_from_kvn,
1179 "CX_DOT_X" => cx_dot_x: kv_from_kvn,
1180 "CX_DOT_Y" => cx_dot_y: kv_from_kvn,
1181 "CX_DOT_Z" => cx_dot_z: kv_from_kvn,
1182 "CX_DOT_X_DOT" => cx_dot_x_dot: kv_from_kvn,
1183 "CY_DOT_X" => cy_dot_x: kv_from_kvn,
1184 "CY_DOT_Y" => cy_dot_y: kv_from_kvn,
1185 "CY_DOT_Z" => cy_dot_z: kv_from_kvn,
1186 "CY_DOT_X_DOT" => cy_dot_x_dot: kv_from_kvn,
1187 "CY_DOT_Y_DOT" => cy_dot_y_dot: kv_from_kvn,
1188 "CZ_DOT_X" => cz_dot_x: kv_from_kvn,
1189 "CZ_DOT_Y" => cz_dot_y: kv_from_kvn,
1190 "CZ_DOT_Z" => cz_dot_z: kv_from_kvn,
1191 "CZ_DOT_X_DOT" => cz_dot_x_dot: kv_from_kvn,
1192 "CZ_DOT_Y_DOT" => cz_dot_y_dot: kv_from_kvn,
1193 "CZ_DOT_Z_DOT" => cz_dot_z_dot: kv_from_kvn,
1194 }, |_| false);
1195
1196 if cx_x.is_some() {
1198 Ok(Some(OpmCovarianceMatrix {
1199 comment,
1200 cov_ref_frame,
1201 cx_x: cx_x.ok_or_else(|| missing_field_err(input, "Covariance Matrix", "CX_X"))?,
1202 cy_x: cy_x.ok_or_else(|| missing_field_err(input, "Covariance Matrix", "CY_X"))?,
1203 cy_y: cy_y.ok_or_else(|| missing_field_err(input, "Covariance Matrix", "CY_Y"))?,
1204 cz_x: cz_x.ok_or_else(|| missing_field_err(input, "Covariance Matrix", "CZ_X"))?,
1205 cz_y: cz_y.ok_or_else(|| missing_field_err(input, "Covariance Matrix", "CZ_Y"))?,
1206 cz_z: cz_z.ok_or_else(|| missing_field_err(input, "Covariance Matrix", "CZ_Z"))?,
1207 cx_dot_x: cx_dot_x
1208 .ok_or_else(|| missing_field_err(input, "Covariance Matrix", "CX_DOT_X"))?,
1209 cx_dot_y: cx_dot_y
1210 .ok_or_else(|| missing_field_err(input, "Covariance Matrix", "CX_DOT_Y"))?,
1211 cx_dot_z: cx_dot_z
1212 .ok_or_else(|| missing_field_err(input, "Covariance Matrix", "CX_DOT_Z"))?,
1213 cx_dot_x_dot: cx_dot_x_dot
1214 .ok_or_else(|| missing_field_err(input, "Covariance Matrix", "CX_DOT_X_DOT"))?,
1215 cy_dot_x: cy_dot_x
1216 .ok_or_else(|| missing_field_err(input, "Covariance Matrix", "CY_DOT_X"))?,
1217 cy_dot_y: cy_dot_y
1218 .ok_or_else(|| missing_field_err(input, "Covariance Matrix", "CY_DOT_Y"))?,
1219 cy_dot_z: cy_dot_z
1220 .ok_or_else(|| missing_field_err(input, "Covariance Matrix", "CY_DOT_Z"))?,
1221 cy_dot_x_dot: cy_dot_x_dot
1222 .ok_or_else(|| missing_field_err(input, "Covariance Matrix", "CY_DOT_X_DOT"))?,
1223 cy_dot_y_dot: cy_dot_y_dot
1224 .ok_or_else(|| missing_field_err(input, "Covariance Matrix", "CY_DOT_Y_DOT"))?,
1225 cz_dot_x: cz_dot_x
1226 .ok_or_else(|| missing_field_err(input, "Covariance Matrix", "CZ_DOT_X"))?,
1227 cz_dot_y: cz_dot_y
1228 .ok_or_else(|| missing_field_err(input, "Covariance Matrix", "CZ_DOT_Y"))?,
1229 cz_dot_z: cz_dot_z
1230 .ok_or_else(|| missing_field_err(input, "Covariance Matrix", "CZ_DOT_Z"))?,
1231 cz_dot_x_dot: cz_dot_x_dot
1232 .ok_or_else(|| missing_field_err(input, "Covariance Matrix", "CZ_DOT_X_DOT"))?,
1233 cz_dot_y_dot: cz_dot_y_dot
1234 .ok_or_else(|| missing_field_err(input, "Covariance Matrix", "CZ_DOT_Y_DOT"))?,
1235 cz_dot_z_dot: cz_dot_z_dot
1236 .ok_or_else(|| missing_field_err(input, "Covariance Matrix", "CZ_DOT_Z_DOT"))?,
1237 }))
1238 } else {
1239 Ok(None)
1240 }
1241}
1242
1243pub fn spacecraft_parameters(input: &mut &str) -> KvnResult<Option<SpacecraftParameters>> {
1245 let mut comment = Vec::new();
1246 let mut mass = None;
1247 let mut solar_rad_area = None;
1248 let mut solar_rad_coeff = None;
1249 let mut drag_area = None;
1250 let mut drag_coeff = None;
1251
1252 parse_block!(input, comment, {
1253 "MASS" => mass: kv_from_kvn,
1254 "SOLAR_RAD_AREA" => solar_rad_area: kv_from_kvn,
1255 "SOLAR_RAD_COEFF" => solar_rad_coeff: kv_from_kvn,
1256 "DRAG_AREA" => drag_area: kv_from_kvn,
1257 "DRAG_COEFF" => drag_coeff: kv_from_kvn,
1258 }, |_| false);
1259
1260 if mass.is_some() || solar_rad_area.is_some() || drag_area.is_some() {
1262 Ok(Some(SpacecraftParameters {
1263 comment,
1264 mass,
1265 solar_rad_area,
1266 solar_rad_coeff,
1267 drag_area,
1268 drag_coeff,
1269 }))
1270 } else {
1271 Ok(None)
1272 }
1273}
1274
1275pub fn user_defined_parameters(input: &mut &str) -> KvnResult<Option<UserDefined>> {
1277 let mut comment = Vec::new();
1278 let mut params = Vec::new();
1279
1280 loop {
1281 let checkpoint = input.checkpoint();
1282 let comments = collect_comments.parse_next(input)?;
1283
1284 let key = match key_token.parse_next(input) {
1285 Ok(k) => k,
1286 Err(_) => {
1287 input.reset(&checkpoint);
1288 break;
1289 }
1290 };
1291
1292 if key.starts_with("USER_DEFINED_") {
1293 comment.extend(comments);
1294 let val = kv_string.parse_next(input)?;
1295 params.push(UserDefinedParameter {
1296 parameter: key.strip_prefix("USER_DEFINED_").unwrap().to_string(),
1297 value: val,
1298 });
1299 } else {
1300 input.reset(&checkpoint);
1302 break;
1303 }
1304
1305 if input.offset_from(&checkpoint) == 0 {
1306 break;
1307 }
1308 }
1309
1310 if params.is_empty() {
1311 Ok(None)
1312 } else {
1313 Ok(Some(UserDefined {
1314 comment,
1315 user_defined: params,
1316 }))
1317 }
1318}
1319
1320#[cfg(test)]
1325mod tests {
1326 use super::*;
1327
1328 #[test]
1329 fn test_keyword() {
1330 let mut input = "OBJECT_NAME";
1331 assert_eq!(keyword.parse_next(&mut input).unwrap(), "OBJECT_NAME");
1332
1333 let mut input = "CCSDS_OPM_VERS";
1334 assert_eq!(keyword.parse_next(&mut input).unwrap(), "CCSDS_OPM_VERS");
1335
1336 let mut input = "X_DOT";
1337 assert_eq!(keyword.parse_next(&mut input).unwrap(), "X_DOT");
1338 }
1339
1340 #[test]
1341 fn test_kvn_value_without_unit() {
1342 let mut input = "SATELLITE-1\n";
1343 let (value, unit) = kvn_value.parse_next(&mut input).unwrap();
1344 assert_eq!(value, "SATELLITE-1");
1345 assert_eq!(unit, None);
1346 }
1347
1348 #[test]
1349 fn test_kvn_value_with_unit() {
1350 let mut input = "6503.514 [km]\n";
1351 let (value, unit) = kvn_value.parse_next(&mut input).unwrap();
1352 assert_eq!(value, "6503.514");
1353 assert_eq!(unit, Some("km"));
1354 }
1355
1356 #[test]
1357 fn test_key_value_line() {
1358 let mut input = "OBJECT_NAME = SATELLITE-1\n";
1359 let (key, value, unit) = key_value_line.parse_next(&mut input).unwrap();
1360 assert_eq!(key, "OBJECT_NAME");
1361 assert_eq!(value, "SATELLITE-1");
1362 assert_eq!(unit, None);
1363
1364 let mut input = "X = 6503.514 [km]\n";
1365 let (key, value, unit) = key_value_line.parse_next(&mut input).unwrap();
1366 assert_eq!(key, "X");
1367 assert_eq!(value, "6503.514");
1368 assert_eq!(unit, Some("km"));
1369 }
1370
1371 #[test]
1372 fn test_comment_line() {
1373 let mut input = "COMMENT This is a comment\n";
1374 let content = comment_line.parse_next(&mut input).unwrap();
1375 assert_eq!(content.trim(), "This is a comment");
1376
1377 let mut input = "COMMENT\n";
1378 let content = comment_line.parse_next(&mut input).unwrap();
1379 assert_eq!(content.trim(), "");
1380 }
1381
1382 #[test]
1383 fn test_block_start() {
1384 let mut input = "META_START\n";
1385 let tag = block_start.parse_next(&mut input).unwrap();
1386 assert_eq!(tag, "META");
1387
1388 let mut input = "COVARIANCE_START\n";
1389 let tag = block_start.parse_next(&mut input).unwrap();
1390 assert_eq!(tag, "COVARIANCE");
1391 }
1392
1393 #[test]
1394 fn test_block_end() {
1395 let mut input = "META_STOP\n";
1396 let tag = block_end.parse_next(&mut input).unwrap();
1397 assert_eq!(tag, "META");
1398
1399 let mut input = "COVARIANCE_END\n";
1400 let tag = block_end.parse_next(&mut input).unwrap();
1401 assert_eq!(tag, "COVARIANCE");
1402 }
1403
1404 #[test]
1405 fn test_expect_key() {
1406 let mut input = "OBJECT_NAME = SAT-1\n";
1407 let (value, unit) = expect_key("OBJECT_NAME").parse_next(&mut input).unwrap();
1408 assert_eq!(value, "SAT-1");
1409 assert_eq!(unit, None);
1410 }
1411
1412 #[test]
1413 fn test_collect_comments() {
1414 let mut input = "COMMENT Line 1\nCOMMENT Line 2\nOBJECT_NAME = SAT\n";
1415 let comments = collect_comments.parse_next(&mut input).unwrap();
1416 assert_eq!(comments, vec!["Line 1", "Line 2"]);
1417 }
1418
1419 #[test]
1420 fn test_raw_line() {
1421 let mut input = "2023-01-01T00:00:00 1000 2000 3000 1.0 2.0 3.0\n";
1422 let content = raw_line.parse_next(&mut input).unwrap();
1423 assert_eq!(content, "2023-01-01T00:00:00 1000 2000 3000 1.0 2.0 3.0");
1424 }
1425
1426 #[test]
1427 fn test_malformed_key_value() {
1428 let mut input = "KEY value\n";
1430 assert!(key_value_line.parse_next(&mut input).is_err());
1431
1432 let mut input_empty = "KEY =\n";
1436 let (_, val, _) = key_value_line.parse_next(&mut input_empty).unwrap();
1437 assert_eq!(val, ""); }
1439
1440 #[test]
1441 fn test_whitespace_variations() {
1442 let mut input = " KEY = value [unit] \n";
1443 let (key, val, unit) = key_value_line.parse_next(&mut input).unwrap();
1444 assert_eq!(key, "KEY");
1445 assert_eq!(val, "value");
1446 assert_eq!(unit, Some("unit"));
1447 }
1448
1449 #[test]
1450 fn test_broken_unit() {
1451 let mut input = "KEY = value [unit\n"; assert!(key_value_line.parse_next(&mut input).is_err());
1453 }
1454
1455 #[test]
1456 fn test_require_field_success() {
1457 let mut input = "";
1458 let value = require_field(&mut input, "TestBlock", "TEST_FIELD", Some(42_u32)).unwrap();
1459 assert_eq!(value, 42);
1460 }
1461
1462 #[test]
1463 fn test_require_field_missing() {
1464 let mut input = "";
1465 let err = require_field::<u32>(&mut input, "TestBlock", "TEST_FIELD", None).unwrap_err();
1466 match err {
1467 ErrMode::Cut(inner) => match *inner.kind {
1468 crate::error::ParserErrorKind::MissingRequiredField { block, field } => {
1469 assert_eq!(block, "TestBlock");
1470 assert_eq!(field, "TEST_FIELD");
1471 }
1472 _ => panic!("Expected MissingRequiredField"),
1473 },
1474 _ => panic!("Expected cut error"),
1475 }
1476 }
1477}