1#![allow(missing_docs)]
2#[macro_use]
3extern crate nom;
4#[macro_use]
5extern crate log;
6
7use nom::{digit, line_ending, not_line_ending, space, IResult};
8use std::fs::File;
9use std::io::Read;
10
11mod uml_print;
12
13#[derive(Debug, Clone, PartialEq)]
15pub enum UMLToken {
16 StartUML,
17 EndUML,
18 Note { position: String, text: String },
19 Parallel { sequences: Vec<UMLTokens> },
20 Message {
21 from: String,
22 to: String,
23 text: Option<String>,
24 colour: Option<String>,
25 },
26 Participant {
27 long_name: Option<String>,
28 short_name: String,
29 },
30 Activate { name: String },
31 Deactivate { name: String },
32 Loop { sequence: UMLTokens, count: u8 },
33 Include { file: String, sequence: UMLTokens },
34 Box { name: String, sequence: UMLTokens },
35 Destroy { name: String },
36 Delay { text: String },
37 Alt { sequences: Vec<UMLTokens> },
38}
39
40#[derive(Debug, Clone, PartialEq)]
41pub struct UMLTokens {
42 pub tokens: Vec<UMLToken>,
43}
44
45impl UMLTokens {
46 pub fn new(tokens: Vec<UMLToken>) -> UMLTokens {
47 UMLTokens { tokens: tokens }
48 }
49}
50
51fn take_until_or_line_ending<'a>(input: &'a [u8],
52 tag: &'static str)
53 -> IResult<&'a [u8], &'a [u8]> {
54 let line_ending_res = not_line_ending(input);
55
56 let output = match line_ending_res {
57 IResult::Done(_, output) => output,
58 _ => return line_ending_res,
59 };
60
61 let take_until_res = take_until!(output, tag);
62 match take_until_res {
63 IResult::Done(_, tag_output) => {
64 let count = tag_output.len();
65 IResult::Done(&input[count..], tag_output)
66 }
67 _ => line_ending_res,
68 }
69}
70
71pub fn parse_uml_file(file: &str, path: Option<&std::path::Path>) -> UMLTokens {
73
74 let old_path = std::env::current_dir().expect("Can't access current directory");
75
76 if let Some(path) = path {
77 info!("Setting current directory to {:?}", path.to_str().unwrap());
78 std::env::set_current_dir(&path).unwrap();
79 }
80
81 let cur_path = std::env::current_dir().unwrap();
82 let file_path = if file.starts_with('/') {
83 file.to_string()
84 } else {
85 format!("{}/{}", cur_path.to_str().unwrap(), file)
86 };
87
88 let mut f = File::open(file_path).unwrap();
89 let mut uml = String::new();
90 f.read_to_string(&mut uml).unwrap();
91
92 uml = uml.replace("\r", "");
94
95 info!("Parsing {}", file);
96 let result = uml_parser(uml.as_bytes());
97
98 let uml_tokens = match result {
99 IResult::Done(_, tokens) => tokens,
100 _ => panic!("{:?}", result),
101 };
102 info!("Done parsing {}", file);
103
104 std::env::set_current_dir(&old_path).unwrap();
105
106 uml_tokens
107}
108
109macro_rules! take_until_first_tag (
114 ($i:expr, $($substr:expr),+) => ({
115 use $crate::nom::InputLength;
116 use $crate::nom::FindSubstring;
117 use $crate::nom::Slice;
118
119enum P {
124 Nothing,
125 Found(usize),
126 NeedMore(usize),
127 }
128
129 let res = P::Nothing;
130
131 $(let res = if $substr.input_len() > $i.input_len() {
132 match res {
133 P::Nothing => P::NeedMore($substr.input_len()),
134 P::NeedMore(old) if old > $substr.input_len() => P::NeedMore($substr.input_len()),
135 _ => res,
136 }
137 } else if let Some(index) = $i.find_substring($substr) {
138 match res {
139 P::Nothing | P::NeedMore(_) => P::Found(index),
140 P::Found(old) if old > index => P::Found(index),
141 _ => res
142 }
143 } else {
144 res
145 };)*
146
147 match res {
148 P::Nothing => $crate::nom::IResult::Error($crate::nom::ErrorKind::TakeUntil),
149 P::Found(index) => $crate::nom::IResult::Done($i.slice(index..), $i.slice(0..index)),
150 P::NeedMore(size) => $crate::nom::IResult::Incomplete($crate::nom::Needed::Size(size)),
151 }
152 })
153);
154
155named!(startuml<&[u8], UMLToken>,
156 chain!(
157 space? ~
158 tag!("@startuml") ~
159 space? ~
160 line_ending
161 ,
162 || UMLToken::StartUML
163 )
164);
165
166named!(enduml<&[u8], UMLToken>,
167 chain!(
168 space? ~
169 tag!("@enduml") ~
170 space? ~
171 line_ending
172 ,
173 || UMLToken::EndUML
174 )
175);
176
177named!(include_parser<&[u8], UMLToken>,
178 chain!(
179 space? ~
180 tag!("!include") ~
181 space ~
182 file: map_res!(
183 not_line_ending,
184 std::str::from_utf8
185 ) ~
186 line_ending
187 ,
188 || {
189 let file = file.trim().trim_matches('\"').to_string();
190
191 UMLToken::Include {
192 file: file.clone(),
193 sequence: parse_uml_file(&file, None),
194 }
195 }
196 )
197);
198
199named!(participant_parser<&[u8], UMLToken>,
200 chain!(
201 space? ~
202 alt!(
203 tag!("participant") |
204 tag!("actor")
205 ) ~
206 space ~
207 name: map_res!(
208 apply!(
209 take_until_or_line_ending, " as "
210 ),
211 std::str::from_utf8
212 ) ~
213 short_name: opt!(
214 chain!(
215 tag!(" as ") ~
216 space? ~
217 text: map_res!(
218 not_line_ending,
219 std::str::from_utf8
220 )
221 ,
222 || {
223 text.trim().to_string()
224 }
225 )
226 ) ~
227 line_ending
228 ,
229 || {
230 UMLToken::Participant {
231 long_name: if short_name.is_some() {
232 Some(name.trim().to_string())
233 } else {
234 None
235 },
236 short_name: if short_name.is_some() {
237 short_name.unwrap().trim().to_string()
238 } else {
239 name.trim().to_string()
240 }
241 }
242 }
243 )
244);
245
246named!(note_parser<&[u8], UMLToken>,
247 chain!(
248 space? ~
249 tag!("note") ~
250 position: map_res!(
251 not_line_ending,
252 std::str::from_utf8
253 ) ~
254 line_ending ~
255 text: map_res!(
256 take_until!("end note"),
257 std::str::from_utf8
258 ) ~
259 tag!("end note") ~
260 space? ~
261 line_ending?
262 ,
263 || {
264 UMLToken::Note {
265 position: position.trim().to_string(),
266 text: text.trim().to_string()
267 }
268 }
269 )
270);
271
272named!(loop_parser<&[u8], UMLToken>,
273 chain!(
274 space? ~
275 tag!("loop") ~
276 space? ~
277 count: map_res!(
278 digit,
279 std::str::from_utf8
280 ) ~
281 space? ~
282 line_ending ~
283 sequence: uml_parser ~
284 space? ~
285 line_ending? ~
286 tag!("end") ~
287 not_line_ending ~
288 line_ending
289 ,
290 || {
291 UMLToken::Loop {
292 sequence: sequence,
293 count: count.parse::<u8>().unwrap()
294 }
295 }
296 )
297);
298
299named!(box_parser<&[u8], UMLToken>,
300 chain!(
301 space? ~
302 tag!("box") ~
303 space? ~
304 name: map_res!(
305 not_line_ending,
306 std::str::from_utf8
307 ) ~
308 space? ~
309 line_ending ~
310 sequence: uml_parser ~
311 space? ~
312 line_ending? ~
313 tag!("end box") ~
314 not_line_ending ~
315 line_ending
316 ,
317 || {
318 UMLToken::Box {
319 name: name.trim().to_string(),
320 sequence: sequence,
321 }
322 }
323 )
324);
325
326named!(message_parser<&[u8], UMLToken>,
327 chain!(
328 space? ~
329 participant_1: map_res!(
330 take_until_first_tag!("->", "<-"),
331 std::str::from_utf8
332 ) ~
333 direction: map_res!(
334 alt!(
335 tag!("->") |
336 tag!("<-")
337 ),
338 std::str::from_utf8
339 ) ~
340 participant_2: map_res!(
341 apply!(
342 take_until_or_line_ending, ":"
343 ),
344 std::str::from_utf8
345 ) ~
346 text: opt!(
347 chain!(
348 tag!(":") ~
349 text: map_res!(
350 not_line_ending,
351 std::str::from_utf8
352 )
353 ,
354 || {
355 text.trim().to_string()
356 }
357 )
358 ) ~
359 line_ending
360 ,
361 || {
362 let (from, to) = match direction {
363 "->" => (participant_1, participant_2),
364 "<-" => (participant_2, participant_1),
365 _ => panic!("Unhandled direction: {}", direction)
366 };
367
368 UMLToken::Message {
369 from: from.trim().to_string(),
370 to: to.trim().to_string(),
371 text: text,
372 colour: None
373 }
374 }
375
376 )
377);
378
379named!(par_parser<&[u8], UMLToken>,
380 chain!(
381 space? ~
382 tag!("par") ~
383 not_line_ending ~
384 line_ending ~
385 uml_array: many1!(
386 chain!(
387 tokens: uml_parser ~
388 space? ~
389 line_ending? ~
390 tag!("else")? ~
391 line_ending?
392 ,
393 || {
394 tokens
395 }
396 )
397 ) ~
398 tag!("end") ~
399 not_line_ending ~
400 line_ending
401 ,
402 || {
403 UMLToken::Parallel {
404 sequences: uml_array
405 }
406 }
407 )
408);
409
410named!(alt_parser<&[u8], UMLToken>,
411 chain!(
412 space? ~
413 tag!("alt") ~
414 not_line_ending ~
415 line_ending ~
416 uml_array: many1!(
417 chain!(
418 tokens: uml_parser ~
419 space? ~
420 line_ending? ~
421 tag!("else")? ~
422 line_ending?
423 ,
424 || {
425 tokens
426 }
427 )
428 ) ~
429 tag!("end") ~
430 not_line_ending ~
431 line_ending
432 ,
433 || {
434 UMLToken::Alt {
435 sequences: uml_array
436 }
437 }
438 )
439);
440
441named!(delay_parser<&[u8], UMLToken>,
442 chain!(
443 space? ~
444 tag!("delay") ~
445 text: map_res!(
446 not_line_ending,
447 std::str::from_utf8
448 ) ~
449 line_ending
450 ,
451 || {
452 UMLToken::Delay {
453 text: text.trim().to_string()
454 }
455 }
456 )
457);
458
459named!(activate_parser<&[u8], UMLToken>,
460 chain!(
461 space? ~
462 tag!("activate") ~
463 name: map_res!(
464 not_line_ending,
465 std::str::from_utf8
466 ) ~
467 line_ending
468 ,
469 || {
470 UMLToken::Activate {
471 name: name.trim().to_string()
472 }
473 }
474 )
475);
476
477named!(deactivate_parser<&[u8], UMLToken>,
478 chain!(
479 space? ~
480 tag!("deactivate") ~
481 name: map_res!(
482 not_line_ending,
483 std::str::from_utf8
484 ) ~
485 line_ending
486 ,
487 || {
488 UMLToken::Deactivate {
489 name: name.trim().to_string()
490 }
491 }
492 )
493);
494
495named!(destroy_parser<&[u8], UMLToken>,
496 chain!(
497 space? ~
498 tag!("destroy") ~
499 name: map_res!(
500 not_line_ending,
501 std::str::from_utf8
502 ) ~
503 line_ending
504 ,
505 || {
506 UMLToken::Destroy {
507 name: name.trim().to_string()
508 }
509 }
510 )
511);
512
513named!(pub uml_parser<&[u8], UMLTokens >,
514 chain!(
515 tokens: many1!(
516 chain!(
517 not!(
518 peek!(
519 alt!(
520 tag!("else") |
521 tag!("end")
522 )
523 )
524 ) ~
525 space? ~
526 line_ending? ~
527 token: alt!(
528 startuml |
529 enduml |
530 include_parser |
531 note_parser |
532 participant_parser |
533 par_parser |
534 alt_parser |
535 delay_parser |
536 activate_parser |
537 deactivate_parser |
538 destroy_parser |
539 box_parser |
540 loop_parser |
541 message_parser
542 )
543 ,
544 || {
545 token
546 }
547 )
548 )
549 ,
550 || {
551 UMLTokens::new(tokens)
552 }
553 )
554);
555
556#[cfg(test)]
557mod tests {
558 use super::*;
559 use nom::IResult::Done;
560
561 #[test]
562 fn test_parse_message() {
563 let test_uml = "PERSON_A->PERSON_B\n";
564 let result = ::message_parser(test_uml.as_bytes());
565
566 assert_eq!(result,
567 Done(&[][..],
568 UMLToken::Message {
569 from: "PERSON_A".to_string(),
570 to: "PERSON_B".to_string(),
571 text: None,
572 colour: None,
573 }));
574 }
575
576 #[test]
577 fn test_parse_message_with_description() {
578 let test_uml = "PERSON_A->PERSON_B:Test\n";
579 let result = ::message_parser(test_uml.as_bytes());
580
581 assert_eq!(result,
582 Done(&[][..],
583 UMLToken::Message {
584 from: "PERSON_A".to_string(),
585 to: "PERSON_B".to_string(),
586 text: Some("Test".to_string()),
587 colour: None,
588 }));
589 }
590
591 #[test]
592 fn test_parse_message_with_description_and_spacing() {
593 let test_uml = "PERSON_A -> PERSON_B: Test\n";
594 let result = ::message_parser(test_uml.as_bytes());
595
596 assert_eq!(result,
597 Done(&[][..],
598 UMLToken::Message {
599 from: "PERSON_A".to_string(),
600 to: "PERSON_B".to_string(),
601 text: Some("Test".to_string()),
602 colour: None,
603 }));
604 }
605
606 #[test]
607 fn test_participant_parser() {
608 let test_uml = "participant test\n";
609 let result = ::participant_parser(test_uml.as_bytes());
610
611 assert_eq!(result,
612 Done(&[][..],
613 UMLToken::Participant {
614 short_name: "test".to_string(),
615 long_name: None,
616 }));
617 }
618
619 #[test]
620 fn test_participant_parser_short_name() {
621 let test_uml = "participant \"test name\" as hello\n";
622 let result = ::participant_parser(test_uml.as_bytes());
623
624 assert_eq!(result,
625 Done(&[][..],
626 UMLToken::Participant {
627 short_name: "hello".to_string(),
628 long_name: Some("\"test name\"".to_string()),
629 }));
630 }
631
632 #[test]
633 fn test_tokens_participant_short_name() {
634 let test_uml = r#"participant "test name"
635participant "test name" as hello
636"#;
637
638 let result = ::uml_parser(test_uml.as_bytes());
639
640 assert_eq!(result,
641 Done(&[][..],
642 UMLTokens {
643 tokens: vec![UMLToken::Participant {
644 short_name: "\"test name\"".to_string(),
645 long_name: None,
646 },
647 UMLToken::Participant {
648 short_name: "hello".to_string(),
649 long_name: Some("\"test name\"".to_string()),
650 }],
651 }));
652 }
653
654 #[test]
655 fn test_tokens_msg_short_long() {
656 let test_uml = r#"TESTA->TESTB
657TESTB->TESTA: Hello
658"#;
659
660 let result = ::uml_parser(test_uml.as_bytes());
661
662 assert_eq!(result,
663 Done(&[][..],
664 UMLTokens {
665 tokens: vec![UMLToken::Message {
666 from: "TESTA".to_string(),
667 to: "TESTB".to_string(),
668 text: None,
669 colour: None,
670 },
671 UMLToken::Message {
672 from: "TESTB".to_string(),
673 to: "TESTA".to_string(),
674 text: Some("Hello".to_string()),
675 colour: None,
676 }],
677 }));
678 }
679
680 #[test]
681 fn test_actor_parser() {
682 let test_uml = "actor test\n";
683 let result = ::participant_parser(test_uml.as_bytes());
684
685 assert_eq!(result,
686 Done(&[][..],
687 UMLToken::Participant {
688 short_name: "test".to_string(),
689 long_name: None,
690 }));
691 }
692
693 #[test]
694 fn test_note_parser() {
695 let test_uml = "note position\nquick test\nend note\n";
696 let result = ::note_parser(test_uml.as_bytes());
697
698 assert_eq!(result,
699 Done(&[][..],
700 UMLToken::Note {
701 position: "position".to_string(),
702 text: "quick test".to_string(),
703 }));
704 }
705
706 #[test]
707 fn test_activate_parser() {
708 let test_uml = "activate test\n";
709 let result = ::activate_parser(test_uml.as_bytes());
710
711 assert_eq!(result,
712 Done(&[][..], UMLToken::Activate { name: "test".to_string() }));
713 }
714
715 #[test]
716 fn test_deactivate_parser() {
717 let test_uml = "deactivate test\n";
718 let result = ::deactivate_parser(test_uml.as_bytes());
719
720 assert_eq!(result,
721 Done(&[][..], UMLToken::Deactivate { name: "test".to_string() }));
722 }
723
724 #[test]
725 fn test_destroy_parser() {
726 let test_uml = "destroy test\n";
727 let result = ::destroy_parser(test_uml.as_bytes());
728
729 assert_eq!(result,
730 Done(&[][..], UMLToken::Destroy { name: "test".to_string() }));
731 }
732
733 #[test]
734 fn test_par_parser() {
735 let test_uml = r#"par
736 PERSON_A->PERSON_B:Test
737 else
738 note position
739 quick test
740 end note
741 end par
742"#;
743
744 let result = ::par_parser(test_uml.as_bytes());
745
746 assert_eq!(result,
747 Done(&[][..],
748 UMLToken::Parallel {
749 sequences: vec![UMLTokens {
750 tokens: vec![UMLToken::Message {
751 from: "PERSON_A".to_string(),
752 to: "PERSON_B".to_string(),
753 text: Some("Test".to_string()),
754 colour: None,
755 }],
756 },
757 UMLTokens {
758 tokens: vec![UMLToken::Note {
759 position: "position".to_string(),
760 text: "quick test".to_string(),
761 }],
762 }],
763 }))
764 }
765
766 #[test]
767 fn test_nested_par() {
768 let test_uml = r#"par
769 note position
770 outer par
771 end note
772 par
773 note position
774 inner par
775 end note
776 else
777 note position
778 inner
779 end note
780 end par
781 else
782 note position
783 outer else
784 end note
785 end par
786"#;
787
788 let result = ::par_parser(test_uml.as_bytes());
789
790 assert_eq!(result,
791 Done(&[][..],
792 UMLToken::Parallel {
793 sequences: vec![UMLTokens {
794 tokens: vec![UMLToken::Note {
795 position: "position".to_string(),
796 text: "outer par".to_string(),
797 },
798 UMLToken::Parallel {
799 sequences: vec![UMLTokens {
800 tokens: vec![
801 UMLToken::Note {
802 position: "position".to_string(),
803 text: "inner par".to_string()
804 },
805 ],
806 },
807 UMLTokens {
808 tokens: vec![
809 UMLToken::Note {
810 position: "position".to_string(),
811 text: "inner".to_string()
812 },
813 ],
814 }],
815 }],
816 },
817 UMLTokens {
818 tokens: vec![UMLToken::Note {
819 position: "position".to_string(),
820 text: "outer else".to_string(),
821 }],
822 }],
823 }))
824 }
825
826 #[test]
827 fn test_parse_tokens() {
828 let test_uml = r#"PERSON_A->PERSON_B:Test
829 note position
830 quick test
831 end note
832"#;
833
834 let result = ::uml_parser(test_uml.as_bytes());
835
836 assert_eq!(result,
837 Done(&[][..],
838 UMLTokens {
839 tokens: vec![UMLToken::Message {
840 from: "PERSON_A".to_string(),
841 to: "PERSON_B".to_string(),
842 text: Some("Test".to_string()),
843 colour: None,
844 },
845 UMLToken::Note {
846 position: "position".to_string(),
847 text: "quick test".to_string(),
848 }],
849 }));
850 }
851
852 #[test]
853 fn test_loop_parser() {
854 let test_uml = r#"loop 10
855 note position
856 quick test
857 end note
858 end loop
859"#;
860
861 let result = ::loop_parser(test_uml.as_bytes());
862
863 assert_eq!(result,
864 Done(&[][..],
865 UMLToken::Loop {
866 count: 10,
867 sequence: UMLTokens {
868 tokens: vec![UMLToken::Note {
869 position: "position".to_string(),
870 text: "quick test".to_string(),
871 }],
872 },
873 }));
874 }
875
876 #[test]
877 fn test_box_parser() {
878 let test_uml = r#"box test
879 note position
880 quick test
881 end note
882 end box
883"#;
884 let result = ::box_parser(test_uml.as_bytes());
885
886 assert_eq!(result,
887 Done(&[][..],
888 UMLToken::Box {
889 name: "test".to_string(),
890 sequence: UMLTokens {
891 tokens: vec![UMLToken::Note {
892 position: "position".to_string(),
893 text: "quick test".to_string(),
894 }],
895 },
896 }));
897 }
898
899 #[test]
900 fn test_uml_parser() {
901 let test_uml = r#"@startuml
902 participant test1
903 @enduml
904"#;
905 let result = ::uml_parser(test_uml.as_bytes());
906
907 assert_eq!(result,
908 Done(&[][..],
909 UMLTokens {
910 tokens: vec![UMLToken::StartUML,
911 UMLToken::Participant {
912 short_name: "test1".to_string(),
913 long_name: None,
914 },
915 UMLToken::EndUML],
916 }));
917 }
918
919 #[test]
920 fn test_blank_line_before_enduml() {
921 let test_uml = r#"@startuml
922 participant test1
923
924 @enduml
925"#;
926 let result = ::uml_parser(test_uml.as_bytes());
927
928 assert_eq!(result,
929 Done(&[][..],
930 UMLTokens {
931 tokens: vec![UMLToken::StartUML,
932 UMLToken::Participant {
933 short_name: "test1".to_string(),
934 long_name: None,
935 },
936 UMLToken::EndUML],
937 }));
938 }
939
940 #[test]
941 fn test_uml_parser_all() {
942 let test_uml = r#"
943@startuml
944participant test1
945note position
946 quick test
947end note
948actor test
949
950loop 5
951 par test
952 note position
953 inside par
954 end note
955 else
956 note position
957 else clause
958 end note
959 end
960end
961activate test activate
962deactivate test deactivate
963@enduml
964"#;
965 let result = ::uml_parser(test_uml.as_bytes());
966
967 assert_eq!(result,
968 Done(&[][..],
969 UMLTokens {
970 tokens: vec![UMLToken::StartUML,
971 UMLToken::Participant {
972 short_name: "test1".to_string(),
973 long_name: None,
974 },
975 UMLToken::Note {
976 position: "position".to_string(),
977 text: "quick test".to_string(),
978 },
979 UMLToken::Participant {
980 short_name: "test".to_string(),
981 long_name: None,
982 },
983 UMLToken::Loop {
984 count: 5,
985 sequence: UMLTokens {
986 tokens: vec![UMLToken::Parallel {
987 sequences: vec![UMLTokens {
988 tokens: vec![
989 UMLToken::Note {
990 position: "position".to_string(),
991 text: "inside par".to_string()
992 }
993 ],
994 },
995 UMLTokens {
996 tokens: vec![
997 UMLToken::Note {
998 position: "position".to_string(),
999 text: "else clause".to_string()
1000 }
1001 ],
1002 }],
1003 }],
1004 },
1005 },
1006 UMLToken::Activate { name: "test activate".to_string() },
1007 UMLToken::Deactivate {
1008 name: "test deactivate".to_string(),
1009 },
1010 UMLToken::EndUML],
1011 }));
1012 }
1013
1014 #[test]
1015 fn test_delay_token() {
1016 let test_uml = r#"
1017@startuml
1018
1019delay 50
1020
1021@enduml
1022"#;
1023 let result = ::uml_parser(test_uml.as_bytes());
1024
1025 assert_eq!(result,
1026 Done(&[][..],
1027 UMLTokens {
1028 tokens: vec![UMLToken::StartUML,
1029 UMLToken::Delay { text: "50".to_string() },
1030 UMLToken::EndUML],
1031 }));
1032 }
1033
1034 #[test]
1035 fn test_blank_line_before_par() {
1036 let test_uml = r#"
1037@startuml
1038
1039par
1040 PERSON_A->PERSON_B: Hello 1
1041else
1042 PERSON_A->PERSON_B: Hello 2
1043else
1044 PERSON_A->PERSON_B: Hello 3
1045end par
1046@enduml
1047"#;
1048 let result = ::uml_parser(test_uml.as_bytes());
1049
1050 assert_eq!(result,
1051 Done(&[][..],
1052 UMLTokens {
1053 tokens: vec![UMLToken::StartUML,
1054 UMLToken::Parallel {
1055 sequences: vec![UMLTokens {
1056 tokens: vec![
1057 UMLToken::Message {
1058 from: "PERSON_A".to_string(),
1059 to: "PERSON_B".to_string(),
1060 text: Some("Hello 1".to_string()),
1061 colour: None
1062 }
1063 ],
1064 },
1065 UMLTokens {
1066 tokens: vec![
1067 UMLToken::Message {
1068 from: "PERSON_A".to_string(),
1069 to: "PERSON_B".to_string(),
1070 text: Some("Hello 2".to_string()),
1071 colour: None
1072 }
1073 ],
1074 },
1075 UMLTokens {
1076 tokens: vec![
1077 UMLToken::Message {
1078 from: "PERSON_A".to_string(),
1079 to: "PERSON_B".to_string(),
1080 text: Some("Hello 3".to_string()),
1081 colour: None
1082 }
1083 ],
1084 }],
1085 },
1086 UMLToken::EndUML],
1087 }));
1088 }
1089
1090 #[test]
1091 fn test_print_uml() {
1092 let test_uml = r#"@startuml
1093participant test1
1094note left
1095quick test
1096end note
1097participant test
1098loop 5
1099par
1100note left
1101inside par
1102end note
1103else
1104note left
1105else clause
1106end note
1107end par
1108end loop
1109activate test
1110deactivate test
1111alt
1112a->b:Hello
1113b->a
1114else
1115note left
1116second alt
1117end note
1118end alt
1119box test
1120participant contents
1121end box
1122@enduml
1123"#;
1124 let (_, uml_vector) = ::uml_parser(test_uml.as_bytes()).unwrap();
1125
1126 let output_string = format!("{}", uml_vector);
1127
1128 assert_eq!(output_string, test_uml);
1129 }
1130
1131 #[test]
1132 fn test_alt_parser() {
1133 let test_uml = r#"alt
1134 PERSON_A->PERSON_B:Test
1135 else
1136 note position
1137 quick test
1138 end note
1139 end alt
1140"#;
1141
1142 let result = ::alt_parser(test_uml.as_bytes());
1143
1144 assert_eq!(result,
1145 Done(&[][..],
1146 UMLToken::Alt {
1147 sequences: vec![UMLTokens {
1148 tokens: vec![UMLToken::Message {
1149 from: "PERSON_A".to_string(),
1150 to: "PERSON_B".to_string(),
1151 text: Some("Test".to_string()),
1152 colour: None,
1153 }],
1154 },
1155 UMLTokens {
1156 tokens: vec![UMLToken::Note {
1157 position: "position".to_string(),
1158 text: "quick test".to_string(),
1159 }],
1160 }],
1161 }))
1162 }
1163}