use std::{fs, path};
use anyhow::{Context, Result};
use log::{debug, error};
use once_cell::sync::Lazy;
use regex::{Regex, RegexBuilder, RegexSet};
use walkdir::{DirEntry, WalkDir};
pub(crate) struct Strip {
pub comments: bool,
pub whitespace: bool,
}
pub(crate) struct DirCopy<'a> {
pub src: &'a path::Path,
pub dest: &'a path::Path,
pub strip: &'a Strip,
}
static RE_COMMENTS: Lazy<Regex> = Lazy::new(|| {
RegexBuilder::new(r#"^\s*r?['"]{3}[\w\W]*?['"]{3}\n|^\s*#.*\n"#)
.multi_line(true)
.unicode(true)
.build()
.unwrap()
});
static RE_INLINE_COMMENTS: Lazy<Regex> = Lazy::new(|| RegexBuilder::new(r#"\s*#.*"#).unicode(true).build().unwrap());
static RE_WHITESPACE: Lazy<Regex> = Lazy::new(|| {
RegexBuilder::new(r#"^s*\n"#)
.multi_line(true)
.unicode(true)
.build()
.unwrap()
});
pub(crate) fn copy(dir_cp: &DirCopy, include: &RegexSet, exclude: &RegexSet) {
for entry in WalkDir::new(dir_cp.src)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| e.file_type().is_file())
.filter(|e| keep_entry(e, include, exclude))
{
let file = entry.path().to_path_buf();
match copy_file(file, dir_cp) {
Ok(_x) => _x,
Err(e) => error!("\n[ERROR]: {:?}\n", e),
};
}
}
fn keep_entry(entry: &DirEntry, include: &RegexSet, exclude: &RegexSet) -> bool {
let ent = entry.path().to_str().unwrap();
include.is_match(ent) && !exclude.is_match(ent)
}
fn copy_file(file: path::PathBuf, dir_cp: &DirCopy) -> Result<()> {
let src_file = file.clone();
let path_strip_prefix = dir_cp.src.to_path_buf();
let normalized_dest_file = file
.strip_prefix(path_strip_prefix)
.with_context(|| format!("Failed to strip path {:?}", dir_cp.src))?;
let dest_file_target = dir_cp.dest.join(normalized_dest_file);
let target_dir = dest_file_target.parent().unwrap();
fs::create_dir_all(target_dir)?;
if dir_cp.strip.whitespace || dir_cp.strip.comments {
debug!("Copying: {:?}", &dest_file_target);
let mut file_contents = fs::read_to_string(file)?;
let stripped_contents = strip(&mut file_contents, dir_cp.strip)?;
fs::write(&dest_file_target, stripped_contents).with_context(|| {
format!(
"Failed to write stripped file {:?} to {:?}",
&src_file, &dest_file_target
)
})?;
} else {
debug!("Copying: {:?}", &dest_file_target);
fs::copy(&src_file, &dest_file_target)
.with_context(|| format!("Failed to copy {:?} to {:?}", &src_file, &dest_file_target))?;
}
Ok(())
}
fn strip(file_contents: &mut str, strip: &Strip) -> Result<String> {
let mut contents = file_contents.to_string();
if strip.comments {
contents = RE_COMMENTS.replace_all(&contents, "").to_string();
contents = RE_INLINE_COMMENTS.replace_all(&contents, "").to_string();
}
if strip.whitespace {
contents = RE_WHITESPACE.replace_all(&contents, "").to_string();
}
Ok(contents)
}