uml_parser/
lib.rs

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/// Tokens that represent each of the elements of UML that are supported.
14#[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
71/// Parse a UML file and return the `UMLTokens` that were parsed.
72pub 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    // Strip out any \r characters from the file to cope with DOS line endings.
93    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
109/// `take_until_first_tag!(tag, ...) => &[T] -> IResult<&[T], &[T]>`
110///
111/// Reads up to the first provided tag (preferring earlier tags if there's
112/// conflict).
113macro_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
119// This enum tracks the current "best answer" over all provided tags.
120//
121// It has a cryptically short name, since we can't `use P::*` due to a language
122// limitation.
123        enum 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}