| 1 | //! Type and helper to collect the gramamr and rule documentation. |
| 2 | |
| 3 | use pest::iterators::Pairs; |
| 4 | use pest_meta::parser::Rule; |
| 5 | use std::collections::HashMap; |
| 6 | |
| 7 | /// Abstraction for the grammer and rule doc. |
| 8 | #[derive (Debug)] |
| 9 | pub struct DocComment { |
| 10 | /// The grammar documentation is defined at the beginning of a file with //!. |
| 11 | pub grammar_doc: String, |
| 12 | |
| 13 | /// HashMap for store all doc_comments for rules. |
| 14 | /// key is rule name, value is doc_comment. |
| 15 | pub line_docs: HashMap<String, String>, |
| 16 | } |
| 17 | |
| 18 | /// Consume pairs to matches `Rule::grammar_doc`, `Rule::line_doc` into `DocComment` |
| 19 | /// |
| 20 | /// e.g. |
| 21 | /// |
| 22 | /// a pest file: |
| 23 | /// |
| 24 | /// ```ignore |
| 25 | /// //! This is a grammar doc |
| 26 | /// /// line doc 1 |
| 27 | /// /// line doc 2 |
| 28 | /// foo = {} |
| 29 | /// |
| 30 | /// /// line doc 3 |
| 31 | /// bar = {} |
| 32 | /// ``` |
| 33 | /// |
| 34 | /// Then will get: |
| 35 | /// |
| 36 | /// ```ignore |
| 37 | /// grammar_doc = "This is a grammar doc" |
| 38 | /// line_docs = { "foo" : "line doc 1 \nline doc 2" , "bar" : "line doc 3" } |
| 39 | /// ``` |
| 40 | pub fn consume(pairs: Pairs<'_, Rule>) -> DocComment { |
| 41 | let mut grammar_doc = String::new(); |
| 42 | |
| 43 | let mut line_docs: HashMap<String, String> = HashMap::new(); |
| 44 | let mut line_doc = String::new(); |
| 45 | |
| 46 | for pair in pairs { |
| 47 | match pair.as_rule() { |
| 48 | Rule::grammar_doc => { |
| 49 | // grammar_doc > inner_doc |
| 50 | let inner_doc = pair.into_inner().next().unwrap(); |
| 51 | grammar_doc.push_str(inner_doc.as_str()); |
| 52 | grammar_doc.push(' \n' ); |
| 53 | } |
| 54 | Rule::grammar_rule => { |
| 55 | if let Some(inner) = pair.into_inner().next() { |
| 56 | // grammar_rule > line_doc | identifier |
| 57 | match inner.as_rule() { |
| 58 | Rule::line_doc => { |
| 59 | if let Some(inner_doc) = inner.into_inner().next() { |
| 60 | line_doc.push_str(inner_doc.as_str()); |
| 61 | line_doc.push(' \n' ); |
| 62 | } |
| 63 | } |
| 64 | Rule::identifier => { |
| 65 | if !line_doc.is_empty() { |
| 66 | let rule_name = inner.as_str().to_owned(); |
| 67 | |
| 68 | // Remove last \n |
| 69 | line_doc.pop(); |
| 70 | line_docs.insert(rule_name, line_doc.clone()); |
| 71 | line_doc.clear(); |
| 72 | } |
| 73 | } |
| 74 | _ => (), |
| 75 | } |
| 76 | } |
| 77 | } |
| 78 | _ => (), |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | if !grammar_doc.is_empty() { |
| 83 | // Remove last \n |
| 84 | grammar_doc.pop(); |
| 85 | } |
| 86 | |
| 87 | DocComment { |
| 88 | grammar_doc, |
| 89 | line_docs, |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | #[cfg (test)] |
| 94 | mod tests { |
| 95 | use std::collections::HashMap; |
| 96 | |
| 97 | use pest_meta::parser; |
| 98 | use pest_meta::parser::Rule; |
| 99 | |
| 100 | #[test ] |
| 101 | fn test_doc_comment() { |
| 102 | let pairs = match parser::parse(Rule::grammar_rules, include_str!("../tests/test.pest" )) { |
| 103 | Ok(pairs) => pairs, |
| 104 | Err(_) => panic!("error parsing tests/test.pest" ), |
| 105 | }; |
| 106 | |
| 107 | let doc_comment = super::consume(pairs); |
| 108 | |
| 109 | let mut expected = HashMap::new(); |
| 110 | expected.insert("foo" .to_owned(), "Matches foo str, e.g.: `foo`" .to_owned()); |
| 111 | expected.insert( |
| 112 | "bar" .to_owned(), |
| 113 | "Matches bar str \n\n Indent 2, e.g: `bar` or `foobar`" .to_owned(), |
| 114 | ); |
| 115 | expected.insert( |
| 116 | "dar" .to_owned(), |
| 117 | "Matches dar \n\nMatch dar description \n" .to_owned(), |
| 118 | ); |
| 119 | assert_eq!(expected, doc_comment.line_docs); |
| 120 | |
| 121 | assert_eq!( |
| 122 | "A parser for JSON file. \nAnd this is a example for JSON parser. \n\n indent-4-space \n" , |
| 123 | doc_comment.grammar_doc |
| 124 | ); |
| 125 | } |
| 126 | |
| 127 | #[test ] |
| 128 | fn test_empty_grammar_doc() { |
| 129 | assert!(parser::parse(Rule::grammar_rules, "//!" ).is_ok()); |
| 130 | assert!(parser::parse(Rule::grammar_rules, "///" ).is_ok()); |
| 131 | assert!(parser::parse(Rule::grammar_rules, "//" ).is_ok()); |
| 132 | assert!(parser::parse(Rule::grammar_rules, "/// Line Doc" ).is_ok()); |
| 133 | assert!(parser::parse(Rule::grammar_rules, "//! Grammar Doc" ).is_ok()); |
| 134 | assert!(parser::parse(Rule::grammar_rules, "// Comment" ).is_ok()); |
| 135 | } |
| 136 | } |
| 137 | |