use std::path;
fn rename_filename_conflict(mut path: path::PathBuf) -> path::PathBuf {
let file_name = path.file_name().unwrap().to_os_string();
for i in 0.. {
if !path.exists() {
break;
}
path.pop();
let mut file_name = file_name.clone();
file_name.push(&format!("_{}", i));
path.push(file_name);
}
path
}
pub fn fs_copy_with_progress
(
paths: &[P],
to: Q,
mut options: fs_extra::dir::CopyOptions,
mut progress_handler: F,
) -> std::io::Result
where
P: AsRef,
Q: AsRef,
F: FnMut(fs_extra::TransitProcess) -> fs_extra::dir::TransitProcessResult,
{
let total_size = {
let mut sum = 0;
for item in paths {
sum += match fs_extra::dir::get_size(item) {
Ok(s) => s,
Err(e) => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("{}", e),
));
}
}
}
sum
};
let mut info_process = fs_extra::TransitProcess {
copied_bytes: 0,
total_bytes: total_size,
file_bytes_copied: 0,
file_total_bytes: 0,
file_name: String::new(),
dir_name: String::new(),
state: fs_extra::dir::TransitState::Normal,
};
let mut result: u64 = 0;
let mut file_options = fs_extra::file::CopyOptions::new();
file_options.overwrite = options.overwrite;
file_options.skip_exist = options.skip_exist;
file_options.buffer_size = options.buffer_size;
let dir_options = options.clone();
let mut destination = to.as_ref().clone().to_path_buf();
for path in paths {
let file_name = path.as_ref().file_name().unwrap().to_os_string();
destination.push(file_name.clone());
if !options.skip_exist {
destination = rename_filename_conflict(destination);
}
if path.as_ref().is_dir() {
/* create the destination dir */
std::fs::create_dir(&destination)?;
for entry in std::fs::read_dir(path)? {
let entry = entry?;
let entry_path = entry.path();
if entry_path.is_dir() {
let dir_handler = |info: fs_extra::dir::TransitProcess| {
info_process.copied_bytes = result + info.copied_bytes;
info_process.state = info.state;
info_process.file_name = info.file_name;
let result = progress_handler(info_process.clone());
match result {
fs_extra::dir::TransitProcessResult::OverwriteAll => {
options.overwrite = true
}
fs_extra::dir::TransitProcessResult::SkipAll => {
options.skip_exist = true
}
_ => {}
}
result
};
match fs_extra::dir::copy_with_progress(
&entry_path,
&destination,
&dir_options,
dir_handler,
) {
Ok(s) => result += s,
Err(e) => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("{}", e),
));
}
}
} else {
let file_name = entry.file_name();
destination.push(file_name.clone());
let file_handler = |info: fs_extra::file::TransitProcess| {
info_process.copied_bytes = result + info.copied_bytes;
info_process.file_bytes_copied = info.copied_bytes;
progress_handler(info_process.clone());
};
match fs_extra::file::copy_with_progress(
&entry_path,
&destination,
&file_options,
file_handler,
) {
Ok(s) => result += s,
Err(e) => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("{}", e),
));
}
}
destination.pop();
}
}
} else {
let file_handler = |info: fs_extra::file::TransitProcess| {
info_process.copied_bytes = result + info.copied_bytes;
info_process.file_bytes_copied = info.copied_bytes;
progress_handler(info_process.clone());
};
match fs_extra::file::copy_with_progress(
path,
&destination,
&file_options,
file_handler,
) {
Ok(s) => result += s,
Err(e) => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("{}", e),
));
}
}
}
destination.pop();
}
Ok(result)
}
pub fn fs_cut_with_progress(
paths: &[P],
to: Q,
mut options: fs_extra::dir::CopyOptions,
mut progress_handler: F,
) -> std::io::Result
where
P: AsRef,
Q: AsRef,
F: FnMut(fs_extra::TransitProcess) -> fs_extra::dir::TransitProcessResult,
{
let total_size = {
let mut sum = 0;
for item in paths {
sum += match fs_extra::dir::get_size(item) {
Ok(s) => s,
Err(e) => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("{}", e),
));
}
}
}
sum
};
let mut info_process = fs_extra::TransitProcess {
copied_bytes: 0,
total_bytes: total_size,
file_bytes_copied: 0,
file_total_bytes: 0,
file_name: String::new(),
dir_name: String::new(),
state: fs_extra::dir::TransitState::Normal,
};
let mut result: u64 = 0;
#[cfg(target_os = "linux")]
use std::os::linux::fs::MetadataExt;
let to_ino = to.as_ref().metadata()?.st_dev();
let mut destination = to.as_ref().clone().to_path_buf();
let mut file_options = fs_extra::file::CopyOptions::new();
file_options.overwrite = options.overwrite;
file_options.skip_exist = options.skip_exist;
file_options.buffer_size = options.buffer_size;
let dir_options = options.clone();
for path in paths {
let file_name = path.as_ref().file_name().unwrap().to_os_string();
destination.push(file_name.clone());
if !options.skip_exist {
destination = rename_filename_conflict(destination);
}
#[cfg(target_os = "linux")]
{
let path_ino = path.as_ref().metadata()?.st_dev();
/* on the same fs, can do a rename */
if path_ino == to_ino {
std::fs::rename(&path, &destination)?;
result += fs_extra::dir::get_size(&destination).unwrap();
info_process.copied_bytes = result;
destination.pop();
continue;
}
}
if path.as_ref().is_dir() {
/* create the destination dir */
std::fs::create_dir(&destination)?;
for entry in std::fs::read_dir(path)? {
let entry = entry?;
let entry_path = entry.path();
if entry_path.is_dir() {
let dir_handler = |info: fs_extra::dir::TransitProcess| {
info_process.copied_bytes = result + info.copied_bytes;
info_process.state = info.state;
let result = progress_handler(info_process.clone());
match result {
fs_extra::dir::TransitProcessResult::OverwriteAll => {
options.overwrite = true
}
fs_extra::dir::TransitProcessResult::SkipAll => {
options.skip_exist = true
}
_ => {}
}
result
};
match fs_extra::dir::move_dir_with_progress(
&entry_path,
&destination,
&dir_options,
dir_handler,
) {
Ok(s) => result += s,
Err(e) => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("{}", e),
));
}
}
} else {
let file_name = entry.file_name();
destination.push(file_name.clone());
let file_handler = |info: fs_extra::file::TransitProcess| {
info_process.copied_bytes = result + info.copied_bytes;
info_process.file_bytes_copied = info.copied_bytes;
progress_handler(info_process.clone());
};
match fs_extra::file::move_file_with_progress(
&entry_path,
&destination,
&file_options,
file_handler,
) {
Ok(s) => result += s,
Err(e) => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("{}", e),
));
}
}
destination.pop();
}
}
/* remove the source dir */
std::fs::remove_dir_all(path)?;
} else {
let handler = |info: fs_extra::file::TransitProcess| {
info_process.copied_bytes = result + info.copied_bytes;
info_process.file_bytes_copied = info.copied_bytes;
progress_handler(info_process.clone());
};
match fs_extra::file::move_file_with_progress(
path,
&destination,
&file_options,
handler,
) {
Ok(s) => result += s,
Err(e) => {
eprintln!("{}", e);
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("{}", e),
));
}
}
}
destination.pop();
}
Ok(result)
}