use pest::error::Error;
use pest::iterators::Pair;
use pest::Parser;
use std::result;
#[derive(Parser)]
#[grammar = "zones/preprocessor.pest"]
struct ZonePreprocessor;
type Result<T> = result::Result<T, Error<Rule>>;
fn parse_tokens(pair: Pair<Rule>) -> Result<String> {
assert_eq!(pair.as_rule(), Rule::tokens);
let mut result = String::new();
let mut opens = 0;
for pair in pair.into_inner() {
match pair.as_rule() {
Rule::open => {
opens += 1;
result.push_str(pair.as_str());
}
Rule::close => {
opens -= 1;
result.push_str(pair.as_str());
}
Rule::newline | Rule::comment => {
if opens > 0 {
for _i in 0..pair.as_str().len() {
result.push(' ');
}
} else {
result.push_str(pair.as_str());
}
}
_ => result.push_str(pair.as_str()),
}
}
Ok(result)
}
pub(crate) fn preprocess(input: &str) -> Result<String> {
let mut result = String::new();
let file = ZonePreprocessor::parse(Rule::file, input)?.next().unwrap(); for pair in file.into_inner() {
match pair.as_rule() {
Rule::tokens => result.push_str(&parse_tokens(pair)?),
Rule::EOI => (), _ => unreachable!("Unexpected rule: {:?}", pair.as_rule()),
}
}
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn test_preprocessor() {
let tests = vec![
("SOA ( 1 2 3 4 5 6 )", "SOA ( 1 2 3 4 5 6 )"),
(
"SOA ( 1 2 ) ( 3 4 ) ( 5 ) ( 6 )\nA 127.0.0.1",
"SOA ( 1 2 ) ( 3 4 ) ( 5 ) ( 6 )\nA 127.0.0.1",
),
(
"SOA soa soa ( 1\n2\n3\n4\n5\n6)",
"SOA soa soa ( 1 2 3 4 5 6)",
),
(
"SOA ; blah\nA 127.0.0.1",
"SOA ; blah\nA 127.0.0.1",
),
(
"SOA (; blah\nA 127.0.0.1)",
"SOA ( A 127.0.0.1)",
),
(
"SOA ; ( blah\nA 127.0.0.1",
"SOA ; ( blah\nA 127.0.0.1",
),
];
for (input, want) in tests {
match preprocess(input) {
Ok(got) => assert_eq!(got, want, "incorrect result for '{}'", input),
Err(err) => panic!("'{}' failed:\n{}", input, err),
}
}
}
}