pest_generator/
docs.rs

1use pest::iterators::Pairs;
2use pest_meta::parser::Rule;
3use std::collections::HashMap;
4
5#[derive(Debug)]
6pub(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/// ```
36pub(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)]
90mod tests {
91    use std::collections::HashMap;
92
93    use pest_meta::parser;
94    use pest_meta::parser::Rule;
95
96    #[test]
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]
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}