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