use std::io::{BufRead, BufReader};
use regex::Regex;
use super::*;
fn parse_line(r: &Regex, line: &str) -> Vec<String> {
let mut links = vec![];
let matches = r.find_iter(line);
for m in matches {
let m = m.as_str();
let mut pre_v: Vec<&str> = m.split("[").collect();
pre_v.remove(0);
while pre_v.len() > 1 {
let temp_line = format!("[{}", pre_v.pop().unwrap());
let mut temp_links = parse_line(r, temp_line.as_str());
links.append(&mut temp_links);
}
let v: Vec<&str> = pre_v[0].splitn(2, "](").collect();
if v.len() > 1 {
let temp_v = v[1].split(')');
let mut i = 0;
for part in temp_v {
i += part.len();
if part.contains('(') {
i += 1; } else {
break;
}
}
let (link, _) = v[1].split_at(i);
links.push(link.to_string());
}
}
links
}
pub fn parse_files<P: AsRef<Path>>(rootdir: P, pattern: &str, mut files: Vec<PathBuf>)
-> Result<Vec<(PathBuf, PathBuf)>, Error> {
let mut collected_links = vec![];
let r = Regex::new(pattern)
.expect("Failed to build regex.");
let rootdir = rootdir.as_ref();
for file in files.drain(..) {
let file = rootdir.join(file).canonicalize()?;
trace!("Working on links from {:?}", file);
let f = std::fs::File::open(&file)?;
let reader = BufReader::new(f);
for line in reader.lines() {
let line = line?; let matches = parse_line(&r, &line);
for target in matches {
if !target.starts_with("http") {
let target = normalize_link(rootdir,
Path::new(&file),
Path::new(&target))?; let file = normalize_path(Path::new(rootdir),
Path::new(&file))?; collected_links.push((file, target));
}
}
}
}
Ok(collected_links)
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
extern crate tempfile;
#[test]
fn test_parse_line() {
let r = Regex::new(PATTERN)
.expect("Failed to build regex.");
let text = "Duis [ornare](enim) magna";
let links = parse_line(&r, text);
assert_eq!(links[0], "enim");
let text = "Duis [ornare](enim) [magna](foo)";
let links = parse_line(&r, text);
assert_eq!(links.len(), 2);
assert!(links.contains(&String::from("enim")));
assert!(links.contains(&String::from("foo")));
let text = "Integer consectetur neque velit, at.";
let link = parse_line(&r, text);
assert!(link.is_empty());
}
#[test]
fn test_parse_files() {
use std::io::Write;
let tmp_dir = tempfile::tempdir()
.expect("Failed to create tempdir.");
fs::create_dir_all(tmp_dir.path().join("subdir"))
.expect("Failed to create directory structure.");
let filepath = tmp_dir.path().join("subdir/file.md");
let mut file = std::fs::File::create(&filepath)
.expect("Failed to create file.");
let f2path = tmp_dir.path().join("file2.md");
let f3path = tmp_dir.path().join("subdir/file3.md");
std::fs::File::create(&f2path)
.expect("Failed to create file2.");
std::fs::File::create(&f3path)
.expect("Failed to create file3.");
write!(file, "Duis [ornare](../file2.md) magna
consectetur neque velit, at.
Duis [ornare](../file2.md) [magna](file3.md)")
.expect("Failed to write to file");
let links = parse_files(&tmp_dir.path().to_path_buf(),
PATTERN,
vec![filepath.to_path_buf()]);
assert!(links.is_ok());
let links = links.unwrap();
assert_eq!(links.len(), 3);
assert!(links.contains( &(
PathBuf::from("subdir/file.md"),
PathBuf::from("file2.md")
)
));
assert!(links.contains( &(
PathBuf::from("subdir/file.md"),
PathBuf::from("subdir/file3.md"),
)
));
}
}