1use std::collections::BTreeMap;
18
19use super::ast::ConditionExpr;
20use super::status::{status_lines, StatusBranch, StatusKind};
21use super::token::{strip_status_prefix, tokenize, SpannedToken, Token};
22use crate::error::ParseError;
23
24pub struct ConditionParser;
26
27impl ConditionParser {
28 pub fn parse(input: &str) -> Result<Option<ConditionExpr>, ParseError> {
43 Self::parse_with_ub(input, &BTreeMap::new())
44 }
45
46 pub fn parse_with_ub(
52 input: &str,
53 ub_definitions: &BTreeMap<String, ConditionExpr>,
54 ) -> Result<Option<ConditionExpr>, ParseError> {
55 let input = input.trim();
56 if input.is_empty() {
57 return Ok(None);
58 }
59
60 let alternatives: Vec<ConditionExpr> = Self::parse_status_branches(input, ub_definitions)?
68 .into_iter()
69 .filter_map(|b| b.condition)
70 .collect();
71
72 match alternatives.len() {
73 0 => Ok(None),
74 1 => Ok(alternatives.into_iter().next()),
75 _ => Ok(Some(ConditionExpr::Or(alternatives))),
76 }
77 }
78
79 pub fn parse_status_branches(
81 input: &str,
82 ub_definitions: &BTreeMap<String, ConditionExpr>,
83 ) -> Result<Vec<StatusBranch>, ParseError> {
84 let mut branches = Vec::new();
85 for line in status_lines(input) {
86 let kind = StatusKind::of(line);
87 let stripped = strip_status_prefix(line);
88 let condition = if kind.is_some() && stripped == line {
90 None
91 } else {
92 let tokens = tokenize(stripped)?;
93 let mut pos = 0;
94 parse_expression(&tokens, &mut pos, ub_definitions)?
95 };
96 branches.push(StatusBranch { kind, condition });
97 }
98 Ok(branches)
99 }
100
101 pub fn parse_raw(input: &str) -> Result<Option<ConditionExpr>, ParseError> {
105 let input = input.trim();
106 if input.is_empty() {
107 return Ok(None);
108 }
109
110 let tokens = tokenize(input)?;
111 if tokens.is_empty() {
112 return Ok(None);
113 }
114
115 let mut pos = 0;
116 let expr = parse_expression(&tokens, &mut pos, &BTreeMap::new())?;
117
118 Ok(expr)
119 }
120}
121
122fn parse_expression(
124 tokens: &[SpannedToken],
125 pos: &mut usize,
126 ub_definitions: &BTreeMap<String, ConditionExpr>,
127) -> Result<Option<ConditionExpr>, ParseError> {
128 parse_xor(tokens, pos, ub_definitions)
129}
130
131fn parse_xor(
133 tokens: &[SpannedToken],
134 pos: &mut usize,
135 ub_definitions: &BTreeMap<String, ConditionExpr>,
136) -> Result<Option<ConditionExpr>, ParseError> {
137 let mut left = match parse_or(tokens, pos, ub_definitions)? {
138 Some(expr) => expr,
139 None => return Ok(None),
140 };
141
142 while *pos < tokens.len() && tokens[*pos].token == Token::Xor {
143 *pos += 1; let right = match parse_or(tokens, pos, ub_definitions)? {
145 Some(expr) => expr,
146 None => return Ok(Some(left)),
147 };
148 left = ConditionExpr::Xor(Box::new(left), Box::new(right));
149 }
150
151 Ok(Some(left))
152}
153
154fn parse_or(
156 tokens: &[SpannedToken],
157 pos: &mut usize,
158 ub_definitions: &BTreeMap<String, ConditionExpr>,
159) -> Result<Option<ConditionExpr>, ParseError> {
160 let mut left = match parse_and(tokens, pos, ub_definitions)? {
161 Some(expr) => expr,
162 None => return Ok(None),
163 };
164
165 while *pos < tokens.len() && tokens[*pos].token == Token::Or {
166 *pos += 1; let right = match parse_and(tokens, pos, ub_definitions)? {
168 Some(expr) => expr,
169 None => return Ok(Some(left)),
170 };
171 left = match left {
173 ConditionExpr::Or(mut exprs) => {
174 exprs.push(right);
175 ConditionExpr::Or(exprs)
176 }
177 _ => ConditionExpr::Or(vec![left, right]),
178 };
179 }
180
181 Ok(Some(left))
182}
183
184fn parse_and(
187 tokens: &[SpannedToken],
188 pos: &mut usize,
189 ub_definitions: &BTreeMap<String, ConditionExpr>,
190) -> Result<Option<ConditionExpr>, ParseError> {
191 let mut left = match parse_not(tokens, pos, ub_definitions)? {
192 Some(expr) => expr,
193 None => return Ok(None),
194 };
195
196 while *pos < tokens.len() {
197 if tokens[*pos].token == Token::And {
198 *pos += 1; let right = match parse_not(tokens, pos, ub_definitions)? {
200 Some(expr) => expr,
201 None => return Ok(Some(left)),
202 };
203 left = flatten_and(left, right);
204 } else if matches!(
205 tokens[*pos].token,
206 Token::ConditionId(_) | Token::LeftParen | Token::Not
207 ) {
208 let right = match parse_not(tokens, pos, ub_definitions)? {
210 Some(expr) => expr,
211 None => return Ok(Some(left)),
212 };
213 left = flatten_and(left, right);
214 } else {
215 break;
216 }
217 }
218
219 Ok(Some(left))
220}
221
222fn flatten_and(left: ConditionExpr, right: ConditionExpr) -> ConditionExpr {
224 match left {
225 ConditionExpr::And(mut exprs) => {
226 exprs.push(right);
227 ConditionExpr::And(exprs)
228 }
229 _ => ConditionExpr::And(vec![left, right]),
230 }
231}
232
233fn parse_not(
235 tokens: &[SpannedToken],
236 pos: &mut usize,
237 ub_definitions: &BTreeMap<String, ConditionExpr>,
238) -> Result<Option<ConditionExpr>, ParseError> {
239 if *pos < tokens.len() && tokens[*pos].token == Token::Not {
240 *pos += 1; let inner = match parse_not(tokens, pos, ub_definitions)? {
242 Some(expr) => expr,
243 None => {
244 return Err(ParseError::UnexpectedToken {
245 position: if *pos < tokens.len() {
246 tokens[*pos].position
247 } else {
248 0
249 },
250 expected: "expression after NOT".to_string(),
251 found: "end of input".to_string(),
252 });
253 }
254 };
255 return Ok(Some(ConditionExpr::Not(Box::new(inner))));
256 }
257 parse_primary(tokens, pos, ub_definitions)
258}
259
260fn parse_primary(
265 tokens: &[SpannedToken],
266 pos: &mut usize,
267 ub_definitions: &BTreeMap<String, ConditionExpr>,
268) -> Result<Option<ConditionExpr>, ParseError> {
269 if *pos >= tokens.len() {
270 return Ok(None);
271 }
272
273 match &tokens[*pos].token {
274 Token::ConditionId(id) => {
275 *pos += 1;
276 if let Some(ub_expr) = ub_definitions.get(id.as_str()) {
278 return Ok(Some(ub_expr.clone()));
279 }
280 Ok(Some(parse_condition_id(id)))
281 }
282 Token::LeftParen => {
283 *pos += 1; let expr = parse_expression(tokens, pos, ub_definitions)?;
285 if *pos < tokens.len() && tokens[*pos].token == Token::RightParen {
287 *pos += 1;
288 }
289 Ok(expr)
290 }
291 _ => Ok(None),
292 }
293}
294
295fn parse_condition_id(id: &str) -> ConditionExpr {
302 if let Ok(num) = id.parse::<u32>() {
304 return ConditionExpr::Ref(num);
305 }
306
307 if let Some(p_pos) = id.find('P') {
309 let num_part = &id[..p_pos];
310 let range_part = &id[p_pos + 1..];
311 if let Ok(pkg_id) = num_part.parse::<u32>() {
312 let (min, max) = parse_package_range(range_part);
313 return ConditionExpr::Package {
314 id: pkg_id,
315 min,
316 max,
317 };
318 }
319 }
320
321 let numeric_part: String = id.chars().take_while(|c| c.is_ascii_digit()).collect();
323 if let Ok(num) = numeric_part.parse::<u32>() {
324 ConditionExpr::Ref(num)
325 } else {
326 ConditionExpr::Ref(0)
327 }
328}
329
330fn parse_package_range(range: &str) -> (u32, u32) {
332 if range.is_empty() {
333 return (0, u32::MAX);
334 }
335 if let Some((min_str, max_str)) = range.split_once("..") {
336 let min = min_str.parse::<u32>().unwrap_or(0);
337 let max = max_str.parse::<u32>().unwrap_or(u32::MAX);
338 (min, max)
339 } else {
340 let n = range.parse::<u32>().unwrap_or(0);
341 (n, n)
342 }
343}
344
345#[cfg(test)]
346mod tests {
347 use super::*;
348 use pretty_assertions::assert_eq;
349
350 #[test]
353 fn test_parse_single_condition() {
354 let result = ConditionParser::parse("[931]").unwrap().unwrap();
355 assert_eq!(result, ConditionExpr::Ref(931));
356 }
357
358 #[test]
359 fn test_parse_with_muss_prefix() {
360 let result = ConditionParser::parse("Muss [494]").unwrap().unwrap();
361 assert_eq!(result, ConditionExpr::Ref(494));
362 }
363
364 #[test]
365 fn test_parse_with_soll_prefix() {
366 let result = ConditionParser::parse("Soll [494]").unwrap().unwrap();
367 assert_eq!(result, ConditionExpr::Ref(494));
368 }
369
370 #[test]
371 fn test_parse_with_kann_prefix() {
372 let result = ConditionParser::parse("Kann [182]").unwrap().unwrap();
373 assert_eq!(result, ConditionExpr::Ref(182));
374 }
375
376 #[test]
377 fn test_parse_with_x_prefix() {
378 let result = ConditionParser::parse("X [567]").unwrap().unwrap();
379 assert_eq!(result, ConditionExpr::Ref(567));
380 }
381
382 #[test]
385 fn test_parse_simple_and() {
386 let result = ConditionParser::parse("[182] ∧ [152]").unwrap().unwrap();
387 assert_eq!(
388 result,
389 ConditionExpr::And(vec![ConditionExpr::Ref(182), ConditionExpr::Ref(152)])
390 );
391 }
392
393 #[test]
394 fn test_parse_simple_or() {
395 let result = ConditionParser::parse("[1] ∨ [2]").unwrap().unwrap();
396 assert_eq!(
397 result,
398 ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)])
399 );
400 }
401
402 #[test]
403 fn test_parse_simple_xor() {
404 let result = ConditionParser::parse("[1] ⊻ [2]").unwrap().unwrap();
405 assert_eq!(
406 result,
407 ConditionExpr::Xor(
408 Box::new(ConditionExpr::Ref(1)),
409 Box::new(ConditionExpr::Ref(2)),
410 )
411 );
412 }
413
414 #[test]
417 fn test_parse_three_way_and() {
418 let result = ConditionParser::parse("[1] ∧ [2] ∧ [3]").unwrap().unwrap();
419 assert_eq!(
420 result,
421 ConditionExpr::And(vec![
422 ConditionExpr::Ref(1),
423 ConditionExpr::Ref(2),
424 ConditionExpr::Ref(3),
425 ])
426 );
427 }
428
429 #[test]
430 fn test_parse_three_way_and_with_prefix() {
431 let result = ConditionParser::parse("Kann [182] ∧ [6] ∧ [570]")
432 .unwrap()
433 .unwrap();
434 assert_eq!(
435 result,
436 ConditionExpr::And(vec![
437 ConditionExpr::Ref(182),
438 ConditionExpr::Ref(6),
439 ConditionExpr::Ref(570),
440 ])
441 );
442 assert_eq!(result.condition_ids(), [6, 182, 570].into());
443 }
444
445 #[test]
446 fn test_parse_multiple_xor() {
447 let result = ConditionParser::parse("[1] ⊻ [2] ⊻ [3] ⊻ [4]")
448 .unwrap()
449 .unwrap();
450 assert_eq!(result.condition_ids(), [1, 2, 3, 4].into());
451 }
452
453 #[test]
456 fn test_parse_parenthesized_expression() {
457 let result = ConditionParser::parse("([1] ∨ [2]) ∧ [3]")
458 .unwrap()
459 .unwrap();
460 assert_eq!(
461 result,
462 ConditionExpr::And(vec![
463 ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]),
464 ConditionExpr::Ref(3),
465 ])
466 );
467 }
468
469 #[test]
470 fn test_parse_nested_parentheses() {
471 let result = ConditionParser::parse("(([1] ∧ [2]) ∨ ([3] ∧ [4])) ∧ [5]")
473 .unwrap()
474 .unwrap();
475 assert_eq!(result.condition_ids(), [1, 2, 3, 4, 5].into());
476 match &result {
478 ConditionExpr::And(exprs) => {
479 assert_eq!(exprs.len(), 2);
480 assert!(matches!(&exprs[0], ConditionExpr::Or(_)));
481 assert_eq!(exprs[1], ConditionExpr::Ref(5));
482 }
483 other => panic!("Expected And, got {other:?}"),
484 }
485 }
486
487 #[test]
490 fn test_and_has_higher_precedence_than_or() {
491 let result = ConditionParser::parse("[1] ∨ [2] ∧ [3]").unwrap().unwrap();
493 assert_eq!(
494 result,
495 ConditionExpr::Or(vec![
496 ConditionExpr::Ref(1),
497 ConditionExpr::And(vec![ConditionExpr::Ref(2), ConditionExpr::Ref(3)]),
498 ])
499 );
500 }
501
502 #[test]
503 fn test_or_has_higher_precedence_than_xor() {
504 let result = ConditionParser::parse("[1] ⊻ [2] ∨ [3]").unwrap().unwrap();
506 assert_eq!(
507 result,
508 ConditionExpr::Xor(
509 Box::new(ConditionExpr::Ref(1)),
510 Box::new(ConditionExpr::Or(vec![
511 ConditionExpr::Ref(2),
512 ConditionExpr::Ref(3),
513 ])),
514 )
515 );
516 }
517
518 #[test]
521 fn test_adjacent_conditions_implicit_and() {
522 let result = ConditionParser::parse("[1] [2]").unwrap().unwrap();
524 assert_eq!(
525 result,
526 ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)])
527 );
528 }
529
530 #[test]
531 fn test_adjacent_conditions_no_space_implicit_and() {
532 let result = ConditionParser::parse("[939][14]").unwrap().unwrap();
534 assert_eq!(
535 result,
536 ConditionExpr::And(vec![ConditionExpr::Ref(939), ConditionExpr::Ref(14)])
537 );
538 }
539
540 #[test]
543 fn test_parse_not() {
544 let result = ConditionParser::parse("NOT [1]").unwrap().unwrap();
545 assert_eq!(result, ConditionExpr::Not(Box::new(ConditionExpr::Ref(1))));
546 }
547
548 #[test]
549 fn test_parse_not_with_and() {
550 let result = ConditionParser::parse("NOT [1] ∧ [2]").unwrap().unwrap();
552 assert_eq!(
553 result,
554 ConditionExpr::And(vec![
555 ConditionExpr::Not(Box::new(ConditionExpr::Ref(1))),
556 ConditionExpr::Ref(2),
557 ])
558 );
559 }
560
561 #[test]
564 fn test_real_world_orders_expression() {
565 let result = ConditionParser::parse("X (([939] [147]) ∨ ([940] [148])) ∧ [567]")
567 .unwrap()
568 .unwrap();
569 assert_eq!(result.condition_ids(), [147, 148, 567, 939, 940].into());
570 }
571
572 #[test]
573 fn test_real_world_xor_expression() {
574 let result = ConditionParser::parse("Muss ([102] ∧ [2006]) ⊻ ([103] ∧ [2005])")
576 .unwrap()
577 .unwrap();
578 assert!(matches!(result, ConditionExpr::Xor(_, _)));
579 assert_eq!(result.condition_ids(), [102, 103, 2005, 2006].into());
580 }
581
582 #[test]
583 fn test_real_world_complex_nested_with_implicit_and() {
584 let result = ConditionParser::parse("([939][14]) ∨ ([940][15])")
586 .unwrap()
587 .unwrap();
588 assert!(matches!(result, ConditionExpr::Or(_)));
589 assert_eq!(result.condition_ids(), [14, 15, 939, 940].into());
590 }
591
592 #[test]
595 fn test_parse_empty_string() {
596 assert!(ConditionParser::parse("").unwrap().is_none());
597 }
598
599 #[test]
600 fn test_parse_whitespace_only() {
601 assert!(ConditionParser::parse(" \t ").unwrap().is_none());
602 }
603
604 #[test]
605 fn test_parse_bare_muss() {
606 assert!(ConditionParser::parse("Muss").unwrap().is_none());
607 }
608
609 #[test]
610 fn test_parse_bare_x() {
611 assert!(ConditionParser::parse("X").unwrap().is_none());
613 }
614
615 #[test]
616 fn test_parse_unmatched_open_paren_graceful() {
617 let result = ConditionParser::parse("([1] ∧ [2]").unwrap().unwrap();
619 assert_eq!(
620 result,
621 ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)])
622 );
623 }
624
625 #[test]
626 fn test_parse_text_and_operator() {
627 let result = ConditionParser::parse("[1] AND [2]").unwrap().unwrap();
628 assert_eq!(
629 result,
630 ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)])
631 );
632 }
633
634 #[test]
635 fn test_parse_text_or_operator() {
636 let result = ConditionParser::parse("[1] OR [2]").unwrap().unwrap();
637 assert_eq!(
638 result,
639 ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)])
640 );
641 }
642
643 #[test]
644 fn test_parse_text_xor_operator() {
645 let result = ConditionParser::parse("[1] XOR [2]").unwrap().unwrap();
646 assert_eq!(
647 result,
648 ConditionExpr::Xor(
649 Box::new(ConditionExpr::Ref(1)),
650 Box::new(ConditionExpr::Ref(2)),
651 )
652 );
653 }
654
655 #[test]
656 fn test_parse_mixed_unicode_and_text_operators() {
657 let result = ConditionParser::parse("[1] ∧ [2] OR [3]").unwrap().unwrap();
658 assert_eq!(
659 result,
660 ConditionExpr::Or(vec![
661 ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]),
662 ConditionExpr::Ref(3),
663 ])
664 );
665 }
666
667 #[test]
668 fn test_parse_deeply_nested() {
669 let result = ConditionParser::parse("((([1])))").unwrap().unwrap();
671 assert_eq!(result, ConditionExpr::Ref(1));
672 }
673
674 #[test]
677 fn test_parse_package_condition_0_1() {
678 let result = ConditionParser::parse("[4P0..1]").unwrap().unwrap();
679 assert_eq!(
680 result,
681 ConditionExpr::Package {
682 id: 4,
683 min: 0,
684 max: 1
685 }
686 );
687 }
688
689 #[test]
690 fn test_parse_package_condition_1_5() {
691 let result = ConditionParser::parse("[10P1..5]").unwrap().unwrap();
692 assert_eq!(
693 result,
694 ConditionExpr::Package {
695 id: 10,
696 min: 1,
697 max: 5
698 }
699 );
700 }
701
702 #[test]
703 fn test_parse_package_in_expression() {
704 let result = ConditionParser::parse("X [4P0..1] ⊻ [5P0..1]")
705 .unwrap()
706 .unwrap();
707 assert_eq!(
708 result,
709 ConditionExpr::Xor(
710 Box::new(ConditionExpr::Package {
711 id: 4,
712 min: 0,
713 max: 1
714 }),
715 Box::new(ConditionExpr::Package {
716 id: 5,
717 min: 0,
718 max: 1
719 }),
720 )
721 );
722 }
723
724 #[test]
725 fn test_parse_package_bare_p() {
726 let result = ConditionParser::parse("[1P]").unwrap().unwrap();
727 assert_eq!(
728 result,
729 ConditionExpr::Package {
730 id: 1,
731 min: 0,
732 max: u32::MAX
733 }
734 );
735 }
736
737 #[test]
738 fn test_condition_ids_extraction_full() {
739 let result = ConditionParser::parse("Muss ([102] ∧ [2006]) ⊻ ([103] ∧ [2005])")
740 .unwrap()
741 .unwrap();
742 let ids = result.condition_ids();
743 assert!(ids.contains(&102));
744 assert!(ids.contains(&103));
745 assert!(ids.contains(&2005));
746 assert!(ids.contains(&2006));
747 assert_eq!(ids.len(), 4);
748 }
749
750 #[test]
753 fn test_parse_ub_inline_expansion() {
754 let ub1_expr = ConditionParser::parse("[931] ∧ [932]").unwrap().unwrap();
755 let mut ub_map = BTreeMap::new();
756 ub_map.insert("UB1".to_string(), ub1_expr.clone());
757
758 let result = ConditionParser::parse_with_ub("X [UB1]", &ub_map)
759 .unwrap()
760 .unwrap();
761 assert_eq!(result, ub1_expr);
762 }
763
764 #[test]
765 fn test_parse_ub_unknown_falls_back() {
766 let ub_map = BTreeMap::new();
767 let result = ConditionParser::parse_with_ub("[UB99]", &ub_map)
768 .unwrap()
769 .unwrap();
770 assert_eq!(result, ConditionExpr::Ref(0));
771 }
772
773 #[test]
774 fn test_parse_with_ub_empty_map_same_as_parse() {
775 let ub_map = BTreeMap::new();
776 let result = ConditionParser::parse_with_ub("X [931] ∧ [932]", &ub_map)
778 .unwrap()
779 .unwrap();
780 let expected = ConditionParser::parse("X [931] ∧ [932]").unwrap().unwrap();
781 assert_eq!(result, expected);
782 }
783
784 #[test]
785 fn test_parse_ub_in_complex_expression() {
786 let ub1_expr = ConditionParser::parse("[931] ∧ [932]").unwrap().unwrap();
788 let mut ub_map = BTreeMap::new();
789 ub_map.insert("UB1".to_string(), ub1_expr);
790
791 let result = ConditionParser::parse_with_ub("X [UB1] ∨ [100]", &ub_map)
792 .unwrap()
793 .unwrap();
794 assert_eq!(
796 result,
797 ConditionExpr::Or(vec![
798 ConditionExpr::And(vec![ConditionExpr::Ref(931), ConditionExpr::Ref(932)]),
799 ConditionExpr::Ref(100),
800 ])
801 );
802 }
803
804 #[test]
805 fn test_parse_ub_multiple_references() {
806 let ub1_expr = ConditionParser::parse("[931]").unwrap().unwrap();
808 let ub2_expr = ConditionParser::parse("[932]").unwrap().unwrap();
809 let mut ub_map = BTreeMap::new();
810 ub_map.insert("UB1".to_string(), ub1_expr);
811 ub_map.insert("UB2".to_string(), ub2_expr);
812
813 let result = ConditionParser::parse_with_ub("X [UB1] ∧ [UB2]", &ub_map)
814 .unwrap()
815 .unwrap();
816 assert_eq!(
817 result,
818 ConditionExpr::And(vec![ConditionExpr::Ref(931), ConditionExpr::Ref(932)])
819 );
820 }
821
822 #[test]
823 fn test_parse_multi_line_alternatives_are_or() {
824 let status = "Muss [315] ∧ [707]\nSoll [8] ∧ [301] ∧ [707]";
827 let result = ConditionParser::parse(status).unwrap().unwrap();
828 assert_eq!(
829 result,
830 ConditionExpr::Or(vec![
831 ConditionExpr::And(vec![ConditionExpr::Ref(315), ConditionExpr::Ref(707)]),
832 ConditionExpr::And(vec![
833 ConditionExpr::Ref(8),
834 ConditionExpr::Ref(301),
835 ConditionExpr::Ref(707),
836 ]),
837 ])
838 );
839 }
840
841 #[test]
842 fn test_parse_multi_line_with_crlf() {
843 let result = ConditionParser::parse("Muss [10]\r\nSoll [20]")
845 .unwrap()
846 .unwrap();
847 assert_eq!(
848 result,
849 ConditionExpr::Or(vec![ConditionExpr::Ref(10), ConditionExpr::Ref(20)])
850 );
851 }
852
853 #[test]
854 fn test_parse_single_line_unchanged() {
855 let result = ConditionParser::parse("Muss [315] ∧ [707]")
857 .unwrap()
858 .unwrap();
859 assert_eq!(
860 result,
861 ConditionExpr::And(vec![ConditionExpr::Ref(315), ConditionExpr::Ref(707)])
862 );
863 }
864
865 fn branches(status: &str) -> Vec<StatusBranch> {
868 ConditionParser::parse_status_branches(status, &BTreeMap::new()).unwrap()
869 }
870
871 #[test]
872 fn test_status_branches_keep_each_lines_status() {
873 assert_eq!(
874 branches("Muss [2119] \r\nSoll [130]"),
875 vec![
876 StatusBranch {
877 kind: Some(StatusKind::Muss),
878 condition: Some(ConditionExpr::Ref(2119)),
879 },
880 StatusBranch {
881 kind: Some(StatusKind::Soll),
882 condition: Some(ConditionExpr::Ref(130)),
883 },
884 ]
885 );
886 }
887
888 #[test]
889 fn test_status_branches_bare_line_has_no_condition() {
890 assert_eq!(
891 branches("Muss [48]\r\nKann"),
892 vec![
893 StatusBranch {
894 kind: Some(StatusKind::Muss),
895 condition: Some(ConditionExpr::Ref(48)),
896 },
897 StatusBranch {
898 kind: Some(StatusKind::Kann),
899 condition: None,
900 },
901 ]
902 );
903 }
904
905 #[test]
906 fn test_status_branches_x_is_muss() {
907 assert_eq!(
908 branches("X [931]"),
909 vec![StatusBranch {
910 kind: Some(StatusKind::Muss),
911 condition: Some(ConditionExpr::Ref(931)),
912 }]
913 );
914 }
915
916 #[test]
917 fn test_status_branches_split_a_second_status_on_the_same_line() {
918 assert_eq!(
920 branches("Muss [56] ∧ [59] Soll [70]"),
921 vec![
922 StatusBranch {
923 kind: Some(StatusKind::Muss),
924 condition: Some(ConditionExpr::And(vec![
925 ConditionExpr::Ref(56),
926 ConditionExpr::Ref(59),
927 ])),
928 },
929 StatusBranch {
930 kind: Some(StatusKind::Soll),
931 condition: Some(ConditionExpr::Ref(70)),
932 },
933 ]
934 );
935 }
936
937 #[test]
938 fn test_status_branches_do_not_split_inside_parentheses() {
939 let status = "Muss ([525]) ∧ ([56] ∧ [59] Soll [70]) ∧ ([54])";
943 let found = branches(status);
944 assert_eq!(found.len(), 1, "{found:?}");
945 assert_eq!(found[0].kind, Some(StatusKind::Muss));
946 assert_eq!(
947 found[0].condition,
948 ConditionParser::parse_raw("([525]) ∧ ([56] ∧ [59] [70]) ∧ ([54])").unwrap()
949 );
950 }
951
952 #[test]
953 fn test_parse_same_line_second_status_is_or() {
954 let result = ConditionParser::parse("Muss [56] ∧ [59] Soll [70]")
955 .unwrap()
956 .unwrap();
957 assert_eq!(
958 result,
959 ConditionExpr::Or(vec![
960 ConditionExpr::And(vec![ConditionExpr::Ref(56), ConditionExpr::Ref(59)]),
961 ConditionExpr::Ref(70),
962 ])
963 );
964 }
965}