1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
use log::*;
use std::fs;
use std::io;
use std::path::Path;
use tempfile::tempdir;

#[cfg(windows)]
mod win_move_file;

fn visit_dirs<P: AsRef<Path>>(
    dir: P,
    cb: &mut dyn for<'r> std::ops::FnMut(&'r fs::DirEntry),
) -> io::Result<()> {
    let dir = dir.as_ref();
    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(&entry);
            }
        }
    }
    Ok(())
}

fn recursive_file_count<P: AsRef<Path>>(dir: P) -> io::Result<u64> {
    let mut count = 0;
    visit_dirs(dir, &mut |_| count += 1)?;
    Ok(count)
}

pub fn move_dir<S: AsRef<Path>, D: AsRef<Path>>(source: S, destination: D) -> io::Result<()> {
    let destination = destination.as_ref();
    let source = source.as_ref();

    if !source.is_dir() {
        return Err(io::Error::new(io::ErrorKind::InvalidInput, "from parameter must be a directory"));
    }

    if source.starts_with(destination) {
        trace!("attempt to move files some levels up!");
        let tempdir = tempdir()?;
        let sub = tempdir.path().join("sub");
        #[cfg(unix)]
        fs::DirBuilder::new().create(&sub)?;
        #[cfg(unix)]
        fs::rename(source, &sub)?;
        #[cfg(windows)]
        win_move_file::rename(source, &sub)?;

        move_dir(&sub, destination).map_err(|err|{
            match fs::rename(&sub,source) {
                Err(err) => err,
                _ => err
            }
        })?;
    } else {
        trace!("rename dir");
        if destination.exists() {
            if destination.is_dir() && recursive_file_count(destination)? == 0 {
                fs::remove_dir_all(destination)?;
                move_dir(source, destination)?;
            }
            else {
                return Err(io::Error::from(io::ErrorKind::AlreadyExists));
            }
        } else {
            #[cfg(unix)]
            fs::DirBuilder::new()
                .recursive(true)
                .create(destination)?;
            #[cfg(windows)]
            fs::DirBuilder::new()
                .recursive(true)
                .create(destination.parent().unwrap())?;
            #[cfg(unix)]
            fs::rename(source, destination)?;
            #[cfg(windows)]
            win_move_file::rename(source, destination)?;
        }
    }

    Ok(())
}