use std::path::{Path, PathBuf}; fn get_dir_content_recursive(p: &Path, filter: &F, pb: &indicatif::ProgressBar, dirs: &mut Vec, files: &mut Vec) -> bool where F: Fn(&Path) -> bool { let mut add = true; for child in p.read_dir().unwrap() { let child = child.unwrap(); let p = child.path(); if filter(&p) { if child.file_type().unwrap().is_dir() { add = get_dir_content_recursive(&child.path(), filter, pb, dirs, files) && add; } else { files.push(p); pb.inc_length(1); } } else { add = false; } } if add { dirs.push(p.to_path_buf()); pb.inc_length(1); } return add; } pub struct DirContent { pub dirs: Vec, pub files: Vec } pub fn get_dir_content_filtered(p: &Path, ignored: &[String], pb: &indicatif::ProgressBar) -> Result { let base = p.canonicalize().map_err(|e| e.to_string())?; let base_path_len = base.to_str().unwrap().len() + 1; let path_filter = |p: &Path| { let p = p.canonicalize().unwrap(); let p = p.to_string_lossy(); if p.len() < base_path_len { return false; } let rel_path = p.split_at(base_path_len).1; !ignored.iter().any(|i| rel_path.starts_with(i)) }; let mut dirs = Vec::::new(); let mut files = Vec::::new(); get_dir_content_recursive(&base, &path_filter, pb, &mut dirs, &mut files); Ok(DirContent { dirs, files }) } pub fn copy_dir(src_dir: &Path, dst_dir: &Path, ignored: &[String], pb: &indicatif::ProgressBar) -> Result<(), String> { let src_dir_len = src_dir.iter().count(); let trim_fn = |p: PathBuf| { let new_path = p.into_iter().skip(src_dir_len).collect::(); (new_path.iter().count() != 0).then_some(new_path).or_else(|| { pb.set_length(pb.length().unwrap() - 1); None }) }; let DirContent { dirs, files } = get_dir_content_filtered(src_dir, ignored, pb)?; for dir in dirs.into_iter().filter_map(trim_fn) { std::fs::create_dir_all(dst_dir.join(dir)).map_err(|e| e.to_string())?; pb.inc(1); } for file in files.into_iter().filter_map(trim_fn) { std::fs::copy(src_dir.join(&file), dst_dir.join(file)).map_err(|e| e.to_string())?; pb.inc(1); } Ok(()) } pub fn remove_dir(dir: &Path, ignored: &[String], pb: &indicatif::ProgressBar) -> Result<(), String> { let to_remove = get_dir_content_filtered(dir, ignored, pb)?; for file in to_remove.files { std::fs::remove_file(file).map_err(|e| e.to_string())?; pb.inc(1); } for dir in to_remove.dirs { std::fs::remove_dir(dir).map_err(|e| e.to_string())?; pb.inc(1); } Ok(()) }