use std::fs;
use std::path::Path;
pub fn create_directory(path: &Path) -> anyhow::Result<()> {
if !path.exists() {
fs::create_dir_all(path)?;
println!("Created directory: {}", path.display());
}
Ok(())
}
pub fn copy_directory(src: &Path, dst: &Path) -> anyhow::Result<()> {
create_directory(dst)?;
for entry in walkdir::WalkDir::new(src).follow_links(true).into_iter().filter_map(|e| e.ok()) {
let path = entry.path();
if path == src {
continue;
}
let relative = path.strip_prefix(src)?;
let target = dst.join(relative);
if path.is_file() {
if let Some(parent) = target.parent() {
if !parent.exists() {
std::fs::create_dir_all(parent)?;
}
}
std::fs::copy(path, &target)
.map_err(|e| anyhow::anyhow!("Failed to copy {} to {}: {}", path.display(), target.display(), e))?;
println!("Copied: {} -> {}", path.display(), target.display());
} else if path.is_dir() && !target.exists() {
std::fs::create_dir_all(&target)
.map_err(|e| anyhow::anyhow!("Failed to create directory {}: {}", target.display(), e))?;
}
}
println!("Successfully copied {} to {}", src.display(), dst.display());
Ok(())
}
pub fn copy_directory_with_template_processing(src: &Path, dst: &Path) -> anyhow::Result<()> {
create_directory(dst)?;
for entry in walkdir::WalkDir::new(src).follow_links(true).into_iter().filter_map(|e| e.ok()) {
let path = entry.path();
if path == src {
continue;
}
if path.file_name().map_or(false, |name| name == "template.json") {
continue;
}
let relative = path.strip_prefix(src)?;
if path.is_file() {
let file_name = path.file_name().unwrap_or_default();
let file_name_str = file_name.to_string_lossy();
let target_file_name = if file_name_str.ends_with(".template") {
file_name_str.replace(".template", "")
} else {
file_name_str.to_string()
};
let target_path = dst.join(relative.parent().unwrap_or_else(|| Path::new(""))).join(&target_file_name);
if let Some(parent) = target_path.parent() {
if !parent.exists() {
std::fs::create_dir_all(parent)?;
}
}
std::fs::copy(path, &target_path)
.map_err(|e| anyhow::anyhow!("Failed to copy {} to {}: {}", path.display(), target_path.display(), e))?;
if let Some(ext) = target_path.extension() {
if ext == "sh" {
let mut perms = fs::metadata(&target_path)?.permissions();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
perms.set_mode(perms.mode() | 0o111); }
fs::set_permissions(&target_path, perms)?;
}
}
println!("Copied: {} -> {}", path.display(), target_path.display());
} else if path.is_dir() {
let target = dst.join(relative);
if !target.exists() {
std::fs::create_dir_all(&target)
.map_err(|e| anyhow::anyhow!("Failed to create directory {}: {}", target.display(), e))?;
}
}
}
println!("Successfully copied {} to {} with template processing", src.display(), dst.display());
Ok(())
}
pub fn copy_dir_contents(from: &Path, to: &Path) -> anyhow::Result<()> {
for entry in fs::read_dir(from)? {
let entry = entry?;
let file_type = entry.file_type()?;
let from_path = entry.path();
let to_path = to.join(entry.file_name());
if file_type.is_dir() {
create_directory(&to_path)?;
copy_dir_contents(&from_path, &to_path)?;
} else {
fs::copy(&from_path, &to_path)?;
}
}
Ok(())
}
pub fn visit_dirs(dir: &Path, cb: &dyn Fn(&Path) -> anyhow::Result<()>) -> anyhow::Result<()> {
if dir.is_dir() {
for entry in fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
visit_dirs(&path, cb)?;
} else {
cb(&path)?;
}
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
use std::fs::File;
use std::io::Write;
#[test]
fn test_create_directory() -> anyhow::Result<()> {
let temp_dir = tempdir()?;
let test_dir_path = temp_dir.path().join("test_directory");
assert!(!test_dir_path.exists());
create_directory(&test_dir_path)?;
assert!(test_dir_path.exists());
assert!(test_dir_path.is_dir());
create_directory(&test_dir_path)?;
let nested_dir_path = test_dir_path.join("nested").join("directories");
create_directory(&nested_dir_path)?;
assert!(nested_dir_path.exists());
assert!(nested_dir_path.is_dir());
Ok(())
}
#[test]
fn test_copy_directory() -> anyhow::Result<()> {
let temp_dir = tempdir()?;
let src_dir = temp_dir.path().join("src_dir");
create_directory(&src_dir)?;
let file1_path = src_dir.join("file1.txt");
let mut file1 = File::create(&file1_path)?;
writeln!(file1, "This is file 1 content")?;
let subdir_path = src_dir.join("subdir");
create_directory(&subdir_path)?;
let file2_path = subdir_path.join("file2.txt");
let mut file2 = File::create(&file2_path)?;
writeln!(file2, "This is file 2 content in a subdirectory")?;
let dst_dir = temp_dir.path().join("dst_dir");
copy_directory(&src_dir, &dst_dir)?;
assert!(dst_dir.exists());
assert!(dst_dir.is_dir());
let dst_file1 = dst_dir.join("file1.txt");
assert!(dst_file1.exists());
assert!(dst_file1.is_file());
let dst_subdir = dst_dir.join("subdir");
assert!(dst_subdir.exists());
assert!(dst_subdir.is_dir());
let dst_file2 = dst_subdir.join("file2.txt");
assert!(dst_file2.exists());
assert!(dst_file2.is_file());
let content1 = fs::read_to_string(&dst_file1)?;
assert_eq!(content1, "This is file 1 content\n");
let content2 = fs::read_to_string(&dst_file2)?;
assert_eq!(content2, "This is file 2 content in a subdirectory\n");
Ok(())
}
#[test]
fn test_copy_dir_contents() -> anyhow::Result<()> {
let temp_dir = tempdir()?;
let src_dir = temp_dir.path().join("src_contents");
create_directory(&src_dir)?;
let file1_path = src_dir.join("file1.txt");
let mut file1 = File::create(&file1_path)?;
writeln!(file1, "Content file 1")?;
let file2_path = src_dir.join("file2.txt");
let mut file2 = File::create(&file2_path)?;
writeln!(file2, "Content file 2")?;
let subdir_path = src_dir.join("nested");
create_directory(&subdir_path)?;
let nested_file_path = subdir_path.join("nested_file.txt");
let mut nested_file = File::create(&nested_file_path)?;
writeln!(nested_file, "Nested file content")?;
let dst_dir = temp_dir.path().join("dst_contents");
create_directory(&dst_dir)?;
copy_dir_contents(&src_dir, &dst_dir)?;
let dst_file1 = dst_dir.join("file1.txt");
assert!(dst_file1.exists());
assert!(dst_file1.is_file());
let dst_file2 = dst_dir.join("file2.txt");
assert!(dst_file2.exists());
assert!(dst_file2.is_file());
let dst_subdir = dst_dir.join("nested");
assert!(dst_subdir.exists());
assert!(dst_subdir.is_dir());
let dst_nested_file = dst_subdir.join("nested_file.txt");
assert!(dst_nested_file.exists());
assert!(dst_nested_file.is_file());
let content1 = fs::read_to_string(&dst_file1)?;
assert_eq!(content1, "Content file 1\n");
let content2 = fs::read_to_string(&dst_file2)?;
assert_eq!(content2, "Content file 2\n");
let nested_content = fs::read_to_string(&dst_nested_file)?;
assert_eq!(nested_content, "Nested file content\n");
Ok(())
}
#[test]
fn test_visit_dirs() -> anyhow::Result<()> {
let temp_dir = tempdir()?;
let root_dir = temp_dir.path();
let root_file1 = root_dir.join("root_file1.txt");
let mut file1 = File::create(&root_file1)?;
writeln!(file1, "Root file 1 content")?;
let root_file2 = root_dir.join("root_file2.txt");
let mut file2 = File::create(&root_file2)?;
writeln!(file2, "Root file 2 content")?;
let subdir1 = root_dir.join("subdir1");
create_directory(&subdir1)?;
let subdir1_file = subdir1.join("subdir1_file.txt");
let mut sub_file1 = File::create(&subdir1_file)?;
writeln!(sub_file1, "Subdir1 file content")?;
let nested_dir = subdir1.join("nested");
create_directory(&nested_dir)?;
let nested_file = nested_dir.join("nested_file.txt");
let mut nested_f = File::create(&nested_file)?;
writeln!(nested_f, "Nested file content")?;
let subdir2 = root_dir.join("subdir2");
create_directory(&subdir2)?;
let subdir2_file = subdir2.join("subdir2_file.txt");
let mut sub_file2 = File::create(&subdir2_file)?;
writeln!(sub_file2, "Subdir2 file content")?;
let visited_files = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
let visited_files_clone = visited_files.clone();
visit_dirs(root_dir, &|path| {
let mut files = visited_files_clone.lock().unwrap();
files.push(path.to_path_buf());
Ok(())
})?;
let files = visited_files.lock().unwrap();
assert_eq!(files.len(), 5);
assert!(files.iter().any(|p| p == &root_file1));
assert!(files.iter().any(|p| p == &root_file2));
assert!(files.iter().any(|p| p == &subdir1_file));
assert!(files.iter().any(|p| p == &nested_file));
assert!(files.iter().any(|p| p == &subdir2_file));
Ok(())
}
}