#[cfg(test)]
mod test;
mod includer;
mod object;
mod parse;
pub use self::includer::{DebugIncluder, FetchedPage, Includer, NullIncluder};
pub use self::object::{IncludeRef, IncludeVariables, PageRef};
use self::parse::parse_include_block;
use crate::span_wrap::SpanWrap;
use regex::{Regex, RegexBuilder};
lazy_static! {
static ref INCLUDE_REGEX: Regex = {
RegexBuilder::new(r"^\[\[\s*include\s+")
.case_insensitive(true)
.multi_line(true)
.dot_matches_new_line(true)
.build()
.unwrap()
};
}
pub fn include<'t, I, E, F>(
log: &slog::Logger,
input: &'t str,
mut includer: I,
invalid_return: F,
) -> Result<(String, Vec<PageRef<'t>>), E>
where
I: Includer<'t, Error = E>,
F: FnOnce() -> E,
{
let log = &log.new(slog_o!(
"filename" => slog_filename!(),
"lineno" => slog_lineno!(),
"function" => "include",
"text" => str!(input),
));
info!(
log,
"Finding and replacing all instances of include blocks in text"
);
let mut ranges = Vec::new();
let mut includes = Vec::new();
for mtch in INCLUDE_REGEX.find_iter(input) {
let start = mtch.start();
debug!(
log,
"Found include regex match";
"start" => start,
"slice" => mtch.as_str(),
);
match parse_include_block(log, &input[start..], start) {
None => debug!(log, "Unable to parse include regex match"),
Some((include, end)) => {
ranges.push(start..end);
includes.push(include);
}
}
}
let fetched_pages = includer.include_pages(&includes)?;
if includes.len() != fetched_pages.len() {
return Err(invalid_return());
}
let ranges_iter = ranges.into_iter();
let includes_iter = includes.into_iter();
let fetched_iter = fetched_pages.into_iter();
let mut output = String::from(input);
let mut pages = Vec::new();
for ((range, include), fetched) in
ranges_iter.zip(includes_iter).zip(fetched_iter).rev()
{
let (page_ref, _) = include.into();
debug!(
log,
"Replacing range for included page";
"span" => SpanWrap::from(&range),
"site" => page_ref.site(),
"page" => page_ref.page(),
);
if page_ref != fetched.page_ref {
return Err(invalid_return());
}
let message;
let replace_with = match fetched.content {
Some(ref content) => content,
None => {
message = includer.no_such_include(&page_ref)?;
&message
}
};
pages.push(page_ref);
output.replace_range(range, replace_with);
}
pages.reverse();
Ok((output, pages))
}