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 | /// ``` |
36 | 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 ] |
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 | } |
133 | |