use crate::parser::{get_ident, parse_phase2, segment::Segment, Rule};
use pest::iterators::Pair;
#[derive(Debug, Default, PartialEq)]
pub struct Template {
pub name: String,
pub indent_ignored: usize,
pub lines: Vec<TemplateLine>,
}
impl<'a> From<Pair<'a, Rule>> for Template {
fn from(pair: Pair<'a, Rule>) -> Template {
let mut template = Template::default();
for item in pair.into_inner() {
match item.as_rule() {
Rule::template_content => template.lines.push(item.as_str().into()),
Rule::template_decl => template.name = get_ident(item),
Rule::template_line => template.lines.push(item.into()),
Rule::template_terminator => {
template.indent_ignored = item.as_str().matches("-").count()
}
Rule::template_empty_line => template.lines.push(TemplateLine::default()),
Rule::EOI => {}
_ => unreachable!(),
}
}
template
}
}
impl Template {
pub fn placeholder_names(&self) -> Vec<String> {
let mut names = Vec::new();
for line in &self.lines {
for segment in &line.segments {
match segment {
Segment::Placeholder(ref s) => {
if !names.contains(s) {
names.push(s.clone());
}
}
_ => {}
}
}
}
names
}
}
#[derive(Debug, Default, PartialEq)]
pub struct TemplateLine {
pub indentation: String,
pub segments: Vec<Segment>,
}
impl From<&str> for TemplateLine {
fn from(s: &str) -> TemplateLine {
TemplateLine {
indentation: "".into(),
segments: parse_phase2(s).unwrap(),
}
}
}
impl<'a> From<Pair<'a, Rule>> for TemplateLine {
fn from(pair: Pair<'a, Rule>) -> TemplateLine {
let mut indentation = String::new();
let mut segments = vec![];
for item in pair.into_inner() {
match item.as_rule() {
Rule::significant_whitespace => indentation = item.as_str().into(),
Rule::template_content => segments = parse_phase2(item.as_str()).unwrap(),
_ => unreachable!(),
}
}
TemplateLine {
indentation,
segments,
}
}
}
#[cfg(test)]
mod tests {
use crate::parser::parse;
#[test]
fn empty_template() {
use insta::assert_debug_snapshot_matches;
let templates = parse("main =\n----").unwrap();
assert_debug_snapshot_matches!("template.empty_template", templates);
}
#[test]
fn space_indented_content() {
use insta::assert_debug_snapshot_matches;
let templates = parse("main =\n indent4\n indent5\n----").unwrap();
assert_debug_snapshot_matches!("template.indented_content", templates);
}
#[test]
fn tab_indented() {
use insta::assert_debug_snapshot_matches;
let templates = parse("main =\n\tindent1\n\t\tindent2\n-").unwrap();
assert_debug_snapshot_matches!("template.tab_indented", templates);
}
#[test]
fn one_liner() {
use insta::assert_debug_snapshot_matches;
let templates =
parse("main = this is a one-liner and white space at the beginning is ignored")
.unwrap();
assert_debug_snapshot_matches!("template.one_liner", templates);
}
#[test]
fn mixed_indentation() {
use insta::assert_debug_snapshot_matches;
let templates = parse("main =\n \tindent\n----").unwrap();
assert_debug_snapshot_matches!("template.mixed_indentation", templates);
}
#[test]
fn ignore_more_indentation() {
use insta::assert_debug_snapshot_matches;
let templates = parse("main =\n content\n----").unwrap();
assert_debug_snapshot_matches!("template.ignore_more_indentation", templates);
}
#[test]
fn ignore_inner_template() {
use insta::assert_debug_snapshot_matches;
let templates = parse("main =\n main =\n x\n ----\n----").unwrap();
assert_debug_snapshot_matches!("template.ignore_inner_template", templates);
}
#[test]
fn invalid_start() {
assert!(parse("main\n content\n--").is_err())
}
#[test]
fn invalid_end() {
assert!(parse("main =\n content\n").is_err())
}
}