pest_generator/
docs.rs

1//! Type and helper to collect the gramamr and rule documentation.
2
3use pest::iterators::Pairs;
4use pest_meta::parser::Rule;
5use std::collections::HashMap;
6
7/// Abstraction for the grammer and rule doc.
8#[derive(Debug)]
9pub 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/// ```
40pub 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)]
94mod 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}