#[macro_use]
extern crate lazy_static;
extern crate regex;
pub mod error;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use regex::Regex;
use error::Error;
#[derive(Debug, Default)]
pub struct Context<'a> {
included_files: BTreeMap<&'a str, &'a str>,
}
pub type SourceMap<'a> = Vec<FileLine<'a>>;
#[derive(Debug)]
pub struct FileLine<'a> {
pub file: Option<&'a str>,
pub line: usize,
}
impl<'a> Context<'a> {
pub fn new() -> Context<'a> {
Context {
..Default::default()
}
}
pub fn include(mut self, name: &'a str, src: &'a str) -> Self {
self.included_files.insert(name, src);
self
}
pub fn expand(&self, src: &'a str) -> Result<(Vec<&'a str>, SourceMap<'a>), Error> {
let mut expanded_src = Vec::new();
let mut source_map = Vec::new();
self.expand_recursive(
None,
src,
&mut expanded_src,
&mut source_map,
&mut Vec::new(),
&mut BTreeSet::new(),
).map(move |_| (expanded_src, source_map))
}
pub fn expand_to_string(&self, src: &'a str) -> Result<(String, SourceMap<'a>), Error> {
self.expand(src)
.map(|(expanded_src, source_map)| (expanded_src.join("\n"), source_map))
}
pub fn expand_to_bytes(&self, src: &'a str) -> Result<(Vec<&[u8]>, SourceMap<'a>), Error> {
self.expand(src).map(|(expanded_src, source_map)| {
(
expanded_src.into_iter().map(|x| x.as_bytes()).collect(),
source_map,
)
})
}
fn expand_recursive(
&self,
in_file: Option<&'a str>,
src: &'a str,
expanded_src: &mut Vec<&'a str>,
source_map: &mut SourceMap<'a>,
include_stack: &mut Vec<&'a str>,
include_set: &mut BTreeSet<&'a str>,
) -> Result<(), Error> {
lazy_static! {
static ref INCLUDE_RE : Regex = Regex::new(r#"^\s*#\s*include\s+[<"](?P<file>.*)[>"]"#).expect("failed to compile INCLUDE_RE regex");
}
for line in src.lines() {
if let Some(caps) = INCLUDE_RE.captures(line) {
let cap_match = caps.name("file")
.expect("Could not find capture group with name \"file\"");
let included_file = cap_match.as_str();
if include_set.contains(&included_file) {
continue;
}
if include_stack.contains(&included_file) {
let in_file = in_file.map(|s| s.to_string());
let line_num = expanded_src.len();
let problem_include = included_file.to_string();
let include_stack = include_stack.into_iter().map(|s| s.to_string()).collect();
return Err(Error::RecursiveInclude {
in_file: in_file,
line_num: line_num,
problem_include: problem_include,
include_stack: include_stack,
});
}
if let Some(content) = self.included_files.get(included_file) {
include_stack.push(&included_file);
self.expand_recursive(
Some(included_file),
content,
expanded_src,
source_map,
include_stack,
include_set,
)?;
include_stack.pop();
} else {
let in_file = in_file.map(|s| s.to_string());
let line_num = expanded_src.len();
let problem_include = included_file.to_string();
return Err(Error::FileNotFound {
in_file: in_file,
line_num: line_num,
problem_include: problem_include,
});
}
} else {
let line_num = expanded_src.len();
expanded_src.push(line);
source_map.push(FileLine {
file: in_file,
line: line_num,
});
}
}
if let Some(in_file) = in_file {
include_set.insert(in_file);
}
Ok(())
}
}