|  | 
|  | 1 | +use std::{ | 
|  | 2 | +    fs, | 
|  | 3 | +    path::{Path, PathBuf}, | 
|  | 4 | +}; | 
|  | 5 | + | 
|  | 6 | +use anyhow::{anyhow, Context}; | 
|  | 7 | + | 
|  | 8 | +use mithril_client::MithrilResult; | 
|  | 9 | + | 
|  | 10 | +/// Copies a directory and its contents to a new location. | 
|  | 11 | +pub fn copy_dir(source_dir: &Path, target_dir: &Path) -> MithrilResult<PathBuf> { | 
|  | 12 | +    let source_dir_name = source_dir | 
|  | 13 | +        .file_name() | 
|  | 14 | +        .ok_or_else(|| anyhow!("Invalid source directory: {}", source_dir.display()))?; | 
|  | 15 | +    let destination_path = target_dir.join(source_dir_name); | 
|  | 16 | +    copy_dir_contents(source_dir, &destination_path)?; | 
|  | 17 | + | 
|  | 18 | +    Ok(destination_path) | 
|  | 19 | +} | 
|  | 20 | + | 
|  | 21 | +fn copy_dir_contents(source_dir: &Path, target_dir: &Path) -> MithrilResult<()> { | 
|  | 22 | +    fs::create_dir_all(target_dir).with_context(|| { | 
|  | 23 | +        format!( | 
|  | 24 | +            "Failed to create target directory: {}", | 
|  | 25 | +            target_dir.display() | 
|  | 26 | +        ) | 
|  | 27 | +    })?; | 
|  | 28 | + | 
|  | 29 | +    for entry in fs::read_dir(source_dir)? { | 
|  | 30 | +        let entry = entry?; | 
|  | 31 | +        let file_type = entry.file_type()?; | 
|  | 32 | +        let src_path = entry.path(); | 
|  | 33 | +        let dst_path = target_dir.join(entry.file_name()); | 
|  | 34 | + | 
|  | 35 | +        if file_type.is_dir() { | 
|  | 36 | +            copy_dir_contents(&src_path, &dst_path)?; | 
|  | 37 | +        } else { | 
|  | 38 | +            fs::copy(&src_path, &dst_path).with_context(|| { | 
|  | 39 | +                format!( | 
|  | 40 | +                    "Failed to copy file '{}' to '{}'", | 
|  | 41 | +                    src_path.display(), | 
|  | 42 | +                    dst_path.display() | 
|  | 43 | +                ) | 
|  | 44 | +            })?; | 
|  | 45 | +        } | 
|  | 46 | +    } | 
|  | 47 | + | 
|  | 48 | +    Ok(()) | 
|  | 49 | +} | 
|  | 50 | + | 
|  | 51 | +#[cfg(test)] | 
|  | 52 | +mod tests { | 
|  | 53 | +    use std::fs::File; | 
|  | 54 | + | 
|  | 55 | +    use mithril_common::{assert_dir_eq, temp_dir_create}; | 
|  | 56 | + | 
|  | 57 | +    use super::*; | 
|  | 58 | + | 
|  | 59 | +    #[test] | 
|  | 60 | +    fn fails_if_source_does_not_exist() { | 
|  | 61 | +        let temp_dir = temp_dir_create!(); | 
|  | 62 | +        let dir_not_exist = PathBuf::from("dir_not_exist"); | 
|  | 63 | + | 
|  | 64 | +        copy_dir(&dir_not_exist, &temp_dir) | 
|  | 65 | +            .expect_err("Expected error when source directory does not exist"); | 
|  | 66 | +    } | 
|  | 67 | + | 
|  | 68 | +    #[test] | 
|  | 69 | +    fn returns_copied_directory_path() { | 
|  | 70 | +        let temp_dir = temp_dir_create!(); | 
|  | 71 | +        let src = temp_dir.join("dir_to_copy"); | 
|  | 72 | +        fs::create_dir(&src).unwrap(); | 
|  | 73 | +        let dst = temp_dir.join("dst"); | 
|  | 74 | + | 
|  | 75 | +        let copied_dir_path = copy_dir(&src, &dst).unwrap(); | 
|  | 76 | + | 
|  | 77 | +        assert_eq!(copied_dir_path, dst.join("dir_to_copy")); | 
|  | 78 | +    } | 
|  | 79 | + | 
|  | 80 | +    #[test] | 
|  | 81 | +    fn copies_nested_directories_and_files() { | 
|  | 82 | +        let temp_dir = temp_dir_create!(); | 
|  | 83 | +        let src = temp_dir.join("dir_to_copy"); | 
|  | 84 | +        fs::create_dir(&src).unwrap(); | 
|  | 85 | +        File::create(src.join("root.txt")).unwrap(); | 
|  | 86 | + | 
|  | 87 | +        let sub_dir1 = src.join("subdir1"); | 
|  | 88 | +        fs::create_dir(&sub_dir1).unwrap(); | 
|  | 89 | +        File::create(sub_dir1.join("subdir1.txt")).unwrap(); | 
|  | 90 | + | 
|  | 91 | +        let sub_dir2 = src.join("subdir2"); | 
|  | 92 | +        fs::create_dir(&sub_dir2).unwrap(); | 
|  | 93 | +        File::create(sub_dir2.join("subdir2.txt")).unwrap(); | 
|  | 94 | + | 
|  | 95 | +        let dst = temp_dir.join("dst"); | 
|  | 96 | + | 
|  | 97 | +        copy_dir(&src, &dst).unwrap(); | 
|  | 98 | + | 
|  | 99 | +        assert_dir_eq!( | 
|  | 100 | +            &dst, | 
|  | 101 | +            "* dir_to_copy/ | 
|  | 102 | +            ** subdir1/ | 
|  | 103 | +            *** subdir1.txt | 
|  | 104 | +            ** subdir2/ | 
|  | 105 | +            *** subdir2.txt | 
|  | 106 | +            ** root.txt" | 
|  | 107 | +        ); | 
|  | 108 | +    } | 
|  | 109 | +} | 
0 commit comments