• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 /// ```
consume(pairs: Pairs<'_, Rule>) -> DocComment40 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]
test_doc_comment()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]
test_empty_grammar_doc()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