use Error;
use ast;
use regex::Regex;
pub const CODE_BLOCK_REGEX: &'static str = "<%.*?%>";
struct Span {
pub low_index: usize,
pub high_index: usize,
}
#[derive(Debug)]
enum FragmentKind {
Code,
Text,
}
struct Fragment {
kind: FragmentKind,
span: Span,
}
pub fn parse(input: &str) -> Result<ast::Ast, Error> {
let code_block_regex = Regex::new(CODE_BLOCK_REGEX).unwrap();
let code_spans: Vec<_> = code_block_regex.find_iter(input).map(|m| {
Span {
low_index: m.start(),
high_index: m.end(),
}
}).collect();
verify_no_overlapping_spans(&code_spans);
let code_fragments: Vec<_> = code_spans.into_iter().map(|span| {
Fragment { span: span, kind: FragmentKind::Code }
}).collect();
let fragments = if !code_fragments.is_empty() {
fill_in_text_fragments(code_fragments)
} else {
vec![Fragment {
kind: FragmentKind::Text,
span: Span { low_index: 0, high_index: input.len() },
}]
};
let mut fragments = remove_empty_fragments(fragments);
trim_delimiters_from_code_frags(&mut fragments);
let items = fragments.into_iter().map(|frag| {
let frag_text = input[frag.span.low_index..frag.span.high_index].to_string();
let item_kind = match frag.kind {
FragmentKind::Text => ast::ItemKind::Text(frag_text),
FragmentKind::Code => ast::ItemKind::Code(frag_text),
};
ast::Item { kind: item_kind }
}).collect();
Ok(ast::Ast { items: items })
}
fn verify_no_overlapping_spans(_spans: &[Span]) {
}
fn fill_in_text_fragments(code_fragments: Vec<Fragment>) -> Vec<Fragment> {
let mut current_index = 0;
code_fragments.into_iter().flat_map(|code_fragment| {
let high_index = code_fragment.span.high_index;
assert!(code_fragment.span.low_index >= current_index);
let fragments = if code_fragment.span.low_index == current_index {
vec![code_fragment].into_iter()
} else { let text_fragment = Fragment {
kind: FragmentKind::Text,
span: Span { low_index: current_index, high_index: code_fragment.span.low_index },
};
vec![text_fragment, code_fragment].into_iter()
};
current_index = high_index;
fragments
}).collect()
}
fn remove_empty_fragments(fragments: Vec<Fragment>) -> Vec<Fragment> {
fragments.into_iter().filter(|frag| frag.span.low_index != frag.span.high_index).collect()
}
fn trim_delimiters_from_code_frags(fragments: &mut Vec<Fragment>) {
for frag in fragments.iter_mut() {
if let FragmentKind::Code = frag.kind {
frag.span.low_index += 2;
frag.span.high_index -= 2;
}
}
}
#[cfg(test)]
mod test {
use ast::*;
use super::*;
#[test]
fn parses_empty_string() {
assert_eq!(parse("").unwrap(), vec![].into());
}
#[test]
fn parses_standalone_new_lines() {
assert_eq!(parse("\n\n\n").unwrap(), vec![
Item { kind: ItemKind::Text("\n\n\n".to_owned()) },
].into());
}
#[test]
fn parses_standalone_text() {
assert_eq!(parse("hello world").unwrap(), vec![
Item { kind: ItemKind::Text("hello world".to_owned()) },
].into());
}
#[test]
fn parses_standalone_code() {
assert_eq!(parse("<% hello %>").unwrap(), vec![
Item { kind: ItemKind::Code(" hello ".to_owned()) },
].into());
}
}