use pest::iterators::Pairs; use pest_meta::parser::Rule; use std::collections::HashMap; #[derive(Debug)] pub(crate) struct DocComment { pub grammar_doc: String, /// HashMap for store all doc_comments for rules. /// key is rule name, value is doc_comment. pub line_docs: HashMap, } /// Consume pairs to matches `Rule::grammar_doc`, `Rule::line_doc` into `DocComment` /// /// e.g. /// /// a pest file: /// /// ```ignore /// //! This is a grammar doc /// /// line doc 1 /// /// line doc 2 /// foo = {} /// /// /// line doc 3 /// bar = {} /// ``` /// /// Then will get: /// /// ```ignore /// grammar_doc = "This is a grammar doc" /// line_docs = { "foo": "line doc 1\nline doc 2", "bar": "line doc 3" } /// ``` pub(crate) fn consume(pairs: Pairs<'_, Rule>) -> DocComment { let mut grammar_doc = String::new(); let mut line_docs: HashMap = HashMap::new(); let mut line_doc = String::new(); for pair in pairs { match pair.as_rule() { Rule::grammar_doc => { // grammar_doc > inner_doc let inner_doc = pair.into_inner().next().unwrap(); grammar_doc.push_str(inner_doc.as_str()); grammar_doc.push('\n'); } Rule::grammar_rule => { if let Some(inner) = pair.into_inner().next() { // grammar_rule > line_doc | identifier match inner.as_rule() { Rule::line_doc => { if let Some(inner_doc) = inner.into_inner().next() { line_doc.push_str(inner_doc.as_str()); line_doc.push('\n'); } } Rule::identifier => { if !line_doc.is_empty() { let rule_name = inner.as_str().to_owned(); // Remove last \n line_doc.pop(); line_docs.insert(rule_name, line_doc.clone()); line_doc.clear(); } } _ => (), } } } _ => (), } } if !grammar_doc.is_empty() { // Remove last \n grammar_doc.pop(); } DocComment { grammar_doc, line_docs, } } #[cfg(test)] mod tests { use std::collections::HashMap; use pest_meta::parser; use pest_meta::parser::Rule; #[test] fn test_doc_comment() { let pairs = match parser::parse(Rule::grammar_rules, include_str!("../tests/test.pest")) { Ok(pairs) => pairs, Err(_) => panic!("error parsing tests/test.pest"), }; let doc_comment = super::consume(pairs); let mut expected = HashMap::new(); expected.insert("foo".to_owned(), "Matches foo str, e.g.: `foo`".to_owned()); expected.insert( "bar".to_owned(), "Matches bar str\n\n Indent 2, e.g: `bar` or `foobar`".to_owned(), ); expected.insert( "dar".to_owned(), "Matches dar\n\nMatch dar description\n".to_owned(), ); assert_eq!(expected, doc_comment.line_docs); assert_eq!( "A parser for JSON file.\nAnd this is a example for JSON parser.\n\n indent-4-space\n", doc_comment.grammar_doc ); } #[test] fn test_empty_grammar_doc() { assert!(parser::parse(Rule::grammar_rules, "//!").is_ok()); assert!(parser::parse(Rule::grammar_rules, "///").is_ok()); assert!(parser::parse(Rule::grammar_rules, "//").is_ok()); assert!(parser::parse(Rule::grammar_rules, "/// Line Doc").is_ok()); assert!(parser::parse(Rule::grammar_rules, "//! Grammar Doc").is_ok()); assert!(parser::parse(Rule::grammar_rules, "// Comment").is_ok()); } }