diff --git a/src/dir.rs b/src/dir.rs index b21b871..6790a36 100644 --- a/src/dir.rs +++ b/src/dir.rs @@ -588,20 +588,14 @@ where skip_exist: options.skip_exist, buffer_size: options.buffer_size, }; - let mut result_copy: Result; - let mut work = true; - while work { - result_copy = super::file::copy(&file, &path, &file_options); - match result_copy { - Ok(val) => { - result += val; - work = false; - } - Err(err) => { - let err_msg = err.to_string(); - err!(err_msg.as_str(), err.kind) - } + match super::file::copy(&file, &path, &file_options) { + Ok(val) => { + result += val; + } + Err(err) => { + let err_msg = err.to_string(); + err!(err_msg.as_str(), err.kind) } } } @@ -1068,6 +1062,7 @@ where } } let mut result: u64 = 0; + let is_symlink_folder = std::fs::symlink_metadata(&from)?.file_type().is_symlink(); for file in dir_content.files { let to = to.to_path_buf(); let tp = Path::new(&file).strip_prefix(from)?; @@ -1079,20 +1074,24 @@ where buffer_size: options.buffer_size, }; - let mut result_copy: Result; - let mut work = true; - while work { - { - result_copy = super::file::move_file(&file, &path, &file_options); - match result_copy { - Ok(val) => { - result += val; - work = false; - } - Err(err) => { - let err_msg = err.to_string(); - err!(err_msg.as_str(), err.kind) - } + if is_symlink_folder { + match super::file::copy(&file, &path, &file_options) { + Ok(val) => { + result += val; + } + Err(err) => { + let err_msg = err.to_string(); + err!(err_msg.as_str(), err.kind) + } + } + } else { + match super::file::move_file(&file, &path, &file_options) { + Ok(val) => { + result += val; + } + Err(err) => { + let err_msg = err.to_string(); + err!(err_msg.as_str(), err.kind) } } } @@ -1233,6 +1232,7 @@ where let mut result_copy: Result; let mut work = true; let copied_bytes = result; + let is_symlink_folder = std::fs::symlink_metadata(&from)?.file_type().is_symlink(); while work { { let _progress_handler = |info: super::file::TransitProcess| { @@ -1241,12 +1241,21 @@ where progress_handler(info_process.clone()); }; - result_copy = super::file::move_file_with_progress( - &file, - &path, - &file_options, - _progress_handler, - ); + if is_symlink_folder { + result_copy = super::file::copy_with_progress( + &file, + &path, + &file_options, + _progress_handler, + ); + } else { + result_copy = super::file::move_file_with_progress( + &file, + &path, + &file_options, + _progress_handler, + ); + } } match result_copy { Ok(val) => { diff --git a/tests/dir.rs b/tests/dir.rs index bf0dfab..0c5f9a2 100644 --- a/tests/dir.rs +++ b/tests/dir.rs @@ -770,6 +770,86 @@ fn it_copy_content_only_option() { assert!(files_eq(&file3.0, &file3.1)); } +#[test] +fn it_copy_link_folder() { + let test_dir = std::fs::canonicalize(Path::new(TEST_FOLDER)) + .unwrap() + .join("it_copy_link_folder"); + let mut link_path = test_dir.clone(); + let path_to = test_dir.join("out"); + let d_level_1 = ( + test_dir.join("d_level_1"), + link_path.join("out").join("link_folder"), + ); + let d_level_2 = (d_level_1.0.join("d_level_2"), d_level_1.1.join("d_level_2")); + let d_level_3 = (d_level_2.0.join("d_level_3"), d_level_2.1.join("d_level_3")); + link_path = link_path.join("link_folder"); + + let file1 = (d_level_1.0.join("file1.txt"), d_level_1.1.join("file1.txt")); + let file2 = (d_level_2.0.join("file2.txt"), d_level_2.1.join("file2.txt")); + let file3 = (d_level_3.0.join("file3.txt"), d_level_3.1.join("file3.txt")); + + create_all(&d_level_1.0, true).unwrap(); + create_all(&d_level_2.0, true).unwrap(); + create_all(&d_level_3.0, true).unwrap(); + create_all(&path_to, true).unwrap(); + + assert!(path_to.exists()); + assert!(d_level_1.0.exists()); + assert!(d_level_2.0.exists()); + assert!(d_level_3.0.exists()); + + assert!(!d_level_1.1.exists()); + assert!(!d_level_2.1.exists()); + assert!(!d_level_3.1.exists()); + + fs_extra::file::write_all(&file1.0, "content1").unwrap(); + fs_extra::file::write_all(&file2.0, "content2").unwrap(); + fs_extra::file::write_all(&file3.0, "content3").unwrap(); + + assert!(file1.0.exists()); + assert!(file2.0.exists()); + assert!(file3.0.exists()); + + assert!(!file1.1.exists()); + assert!(!file2.1.exists()); + assert!(!file3.1.exists()); + + if link_path.exists() { + fs_extra::file::remove(&link_path).unwrap(); + } + #[cfg(target_family = "unix")] + std::os::unix::fs::symlink(&d_level_1.0, &link_path).unwrap(); + + #[cfg(target_family = "windows")] + std::os::windows::fs::symlink(&d_level_1.0, &link_path).unwrap(); + + let options = CopyOptions::new(); + let result = copy(&link_path, path_to, &options).unwrap(); + + assert_eq!(24, result); + + assert!(d_level_1.0.exists()); + assert!(d_level_2.0.exists()); + assert!(d_level_3.0.exists()); + + assert!(d_level_1.1.exists()); + assert!(d_level_2.1.exists()); + assert!(d_level_3.1.exists()); + + assert!(file1.0.exists()); + assert!(file2.0.exists()); + assert!(file3.0.exists()); + + assert!(file1.1.exists()); + assert!(file2.1.exists()); + assert!(file3.1.exists()); + + assert!(files_eq(&file1.0, &file1.1)); + assert!(files_eq(&file2.0, &file2.1)); + assert!(files_eq(&file3.0, &file3.1)); +} + #[test] fn it_copy_progress_work() { let mut path_from = PathBuf::from(TEST_FOLDER); @@ -1552,6 +1632,104 @@ fn it_copy_with_progress_content_only_option() { } } +#[test] +fn it_copy_with_progress_link_folder() { + let test_dir = std::fs::canonicalize(Path::new(TEST_FOLDER)) + .unwrap() + .join("it_copy_with_progress_link_folder"); + let mut link_path = test_dir.clone(); + let path_to = test_dir.join("out"); + let d_level_1 = ( + test_dir.join("d_level_1"), + link_path.join("out").join("link_folder"), + ); + let d_level_2 = (d_level_1.0.join("d_level_2"), d_level_1.1.join("d_level_2")); + let d_level_3 = (d_level_2.0.join("d_level_3"), d_level_2.1.join("d_level_3")); + link_path = link_path.join("link_folder"); + + let file1 = (d_level_1.0.join("file1.txt"), d_level_1.1.join("file1.txt")); + let file2 = (d_level_2.0.join("file2.txt"), d_level_2.1.join("file2.txt")); + let file3 = (d_level_3.0.join("file3.txt"), d_level_3.1.join("file3.txt")); + + create_all(&d_level_1.0, true).unwrap(); + create_all(&d_level_2.0, true).unwrap(); + create_all(&d_level_3.0, true).unwrap(); + create_all(&path_to, true).unwrap(); + + assert!(path_to.exists()); + assert!(d_level_1.0.exists()); + assert!(d_level_2.0.exists()); + assert!(d_level_3.0.exists()); + + assert!(!d_level_1.1.exists()); + assert!(!d_level_2.1.exists()); + assert!(!d_level_3.1.exists()); + + fs_extra::file::write_all(&file1.0, "content1").unwrap(); + fs_extra::file::write_all(&file2.0, "content2").unwrap(); + fs_extra::file::write_all(&file3.0, "content3").unwrap(); + + assert!(file1.0.exists()); + assert!(file2.0.exists()); + assert!(file3.0.exists()); + + assert!(!file1.1.exists()); + assert!(!file2.1.exists()); + assert!(!file3.1.exists()); + + if link_path.exists() { + fs_extra::file::remove(&link_path).unwrap(); + } + #[cfg(target_family = "unix")] + std::os::unix::fs::symlink(&d_level_1.0, &link_path).unwrap(); + + #[cfg(target_family = "windows")] + std::os::windows::fs::symlink(&d_level_1.0, &link_path).unwrap(); + + let options = CopyOptions::new(); + let (tx, rx) = mpsc::channel(); + let result = thread::spawn(move || { + let func_test = |process_info: TransitProcess| { + tx.send(process_info).unwrap(); + TransitProcessResult::ContinueOrAbort + }; + + let result = copy_with_progress(&link_path, &path_to, &options, func_test).unwrap(); + + assert_eq!(24, result); + + assert!(d_level_1.0.exists()); + assert!(d_level_2.0.exists()); + assert!(d_level_3.0.exists()); + + assert!(d_level_1.1.exists()); + assert!(d_level_2.1.exists()); + assert!(d_level_3.1.exists()); + + assert!(file1.0.exists()); + assert!(file2.0.exists()); + assert!(file3.0.exists()); + + assert!(file1.1.exists()); + assert!(file2.1.exists()); + assert!(file3.1.exists()); + + assert!(files_eq(&file1.0, &file1.1)); + assert!(files_eq(&file2.0, &file2.1)); + assert!(files_eq(&file3.0, &file3.1)); + }).join(); + + match result { + Ok(_) => {} + Err(err) => panic!(err), + } + + match rx.recv() { + Err(_) => panic!("Errors should not be!"), + _ => {} + } +} + #[test] fn it_copy_inside_work_target_dir_not_exist() { let path_root = Path::new(TEST_FOLDER); @@ -2264,6 +2442,89 @@ fn it_move_content_only_option() { assert!(file2.1.exists()); assert!(file3.1.exists()); } + +#[test] +fn it_move_link_folder() { + let test_dir = std::fs::canonicalize(Path::new(TEST_FOLDER)) + .unwrap() + .join("it_move_link_folder"); + let mut link_path = test_dir.clone(); + let path_to = test_dir.join("out"); + let d_level_1 = ( + test_dir.join("d_level_1"), + link_path.join("out").join("link_folder"), + ); + let d_level_2 = (d_level_1.0.join("d_level_2"), d_level_1.1.join("d_level_2")); + let d_level_3 = (d_level_2.0.join("d_level_3"), d_level_2.1.join("d_level_3")); + link_path = link_path.join("link_folder"); + + let file1 = (d_level_1.0.join("file1.txt"), d_level_1.1.join("file1.txt")); + let file2 = (d_level_2.0.join("file2.txt"), d_level_2.1.join("file2.txt")); + let file3 = (d_level_3.0.join("file3.txt"), d_level_3.1.join("file3.txt")); + + create_all(&d_level_1.0, true).unwrap(); + create_all(&d_level_2.0, true).unwrap(); + create_all(&d_level_3.0, true).unwrap(); + create_all(&path_to, true).unwrap(); + + assert!(path_to.exists()); + assert!(d_level_1.0.exists()); + assert!(d_level_2.0.exists()); + assert!(d_level_3.0.exists()); + + assert!(!d_level_1.1.exists()); + assert!(!d_level_2.1.exists()); + assert!(!d_level_3.1.exists()); + + fs_extra::file::write_all(&file1.0, "content1").unwrap(); + fs_extra::file::write_all(&file2.0, "content2").unwrap(); + fs_extra::file::write_all(&file3.0, "content3").unwrap(); + + assert!(file1.0.exists()); + assert!(file2.0.exists()); + assert!(file3.0.exists()); + + assert!(!file1.1.exists()); + assert!(!file2.1.exists()); + assert!(!file3.1.exists()); + + if link_path.exists() { + fs_extra::file::remove(&link_path).unwrap(); + } + #[cfg(target_family = "unix")] + std::os::unix::fs::symlink(&d_level_1.0, &link_path).unwrap(); + + #[cfg(target_family = "windows")] + std::os::windows::fs::symlink(&d_level_1.0, &link_path).unwrap(); + assert!(link_path.exists()); + + let options = CopyOptions::new(); + let result = move_dir(&link_path, path_to, &options).unwrap(); + + assert_eq!(24, result); + + assert!(!link_path.exists()); + assert!(d_level_1.0.exists()); + assert!(d_level_2.0.exists()); + assert!(d_level_3.0.exists()); + + assert!(d_level_1.1.exists()); + assert!(d_level_2.1.exists()); + assert!(d_level_3.1.exists()); + + assert!(file1.0.exists()); + assert!(file2.0.exists()); + assert!(file3.0.exists()); + + assert!(file1.1.exists()); + assert!(file2.1.exists()); + assert!(file3.1.exists()); + + assert!(files_eq(&file1.0, &file1.1)); + assert!(files_eq(&file2.0, &file2.1)); + assert!(files_eq(&file3.0, &file3.1)); +} + #[test] fn it_move_progress_work() { let mut path_from = PathBuf::from(TEST_FOLDER); @@ -4751,3 +5012,103 @@ fn it_move_with_progress_content_only_option() { _ => {} } } + +#[test] +fn it_move_with_progress_link_folder() { + let test_dir = std::fs::canonicalize(Path::new(TEST_FOLDER)) + .unwrap() + .join("it_move_with_progress_link_folder"); + let mut link_path = test_dir.clone(); + let path_to = test_dir.join("out"); + let d_level_1 = ( + test_dir.join("d_level_1"), + link_path.join("out").join("link_folder"), + ); + let d_level_2 = (d_level_1.0.join("d_level_2"), d_level_1.1.join("d_level_2")); + let d_level_3 = (d_level_2.0.join("d_level_3"), d_level_2.1.join("d_level_3")); + link_path = link_path.join("link_folder"); + + let file1 = (d_level_1.0.join("file1.txt"), d_level_1.1.join("file1.txt")); + let file2 = (d_level_2.0.join("file2.txt"), d_level_2.1.join("file2.txt")); + let file3 = (d_level_3.0.join("file3.txt"), d_level_3.1.join("file3.txt")); + + create_all(&d_level_1.0, true).unwrap(); + create_all(&d_level_2.0, true).unwrap(); + create_all(&d_level_3.0, true).unwrap(); + create_all(&path_to, true).unwrap(); + + assert!(path_to.exists()); + assert!(d_level_1.0.exists()); + assert!(d_level_2.0.exists()); + assert!(d_level_3.0.exists()); + + assert!(!d_level_1.1.exists()); + assert!(!d_level_2.1.exists()); + assert!(!d_level_3.1.exists()); + + fs_extra::file::write_all(&file1.0, "content1").unwrap(); + fs_extra::file::write_all(&file2.0, "content2").unwrap(); + fs_extra::file::write_all(&file3.0, "content3").unwrap(); + + assert!(file1.0.exists()); + assert!(file2.0.exists()); + assert!(file3.0.exists()); + + assert!(!file1.1.exists()); + assert!(!file2.1.exists()); + assert!(!file3.1.exists()); + + if link_path.exists() { + fs_extra::file::remove(&link_path).unwrap(); + } + #[cfg(target_family = "unix")] + std::os::unix::fs::symlink(&d_level_1.0, &link_path).unwrap(); + + #[cfg(target_family = "windows")] + std::os::windows::fs::symlink(&d_level_1.0, &link_path).unwrap(); + assert!(link_path.exists()); + + let options = CopyOptions::new(); + let (tx, rx) = mpsc::channel(); + let result = thread::spawn(move || { + let func_test = |process_info: TransitProcess| { + tx.send(process_info).unwrap(); + TransitProcessResult::ContinueOrAbort + }; + + let result = move_dir_with_progress(&link_path, &path_to, &options, func_test).unwrap(); + + assert_eq!(24, result); + + assert!(!link_path.exists()); + assert!(d_level_1.0.exists()); + assert!(d_level_2.0.exists()); + assert!(d_level_3.0.exists()); + + assert!(d_level_1.1.exists()); + assert!(d_level_2.1.exists()); + assert!(d_level_3.1.exists()); + + assert!(file1.0.exists()); + assert!(file2.0.exists()); + assert!(file3.0.exists()); + + assert!(file1.1.exists()); + assert!(file2.1.exists()); + assert!(file3.1.exists()); + + assert!(files_eq(&file1.0, &file1.1)); + assert!(files_eq(&file2.0, &file2.1)); + assert!(files_eq(&file3.0, &file3.1)); + }).join(); + + match result { + Ok(_) => {} + Err(err) => panic!(err), + } + + match rx.recv() { + Err(_) => panic!("Errors should not be!"), + _ => {} + } +} diff --git a/tests/file.rs b/tests/file.rs index c7fdb36..14b92de 100644 --- a/tests/file.rs +++ b/tests/file.rs @@ -1,11 +1,11 @@ // use std::io::{ErrorKind, Result}; use std::path::{Path, PathBuf}; -use std::thread; use std::sync::mpsc; +use std::thread; extern crate fs_extra; -use fs_extra::file::*; use fs_extra::error::*; +use fs_extra::file::*; const TEST_FOLDER: &'static str = "./tests/temp/file"; @@ -291,6 +291,39 @@ fn it_copy_exist_overwrite_and_skip_exist() { } } +#[test] +fn it_copy_link_file() { + let mut test_file = std::fs::canonicalize(PathBuf::from(TEST_FOLDER)).unwrap(); + test_file.push("it_copy_link_file"); + let mut test_link = test_file.clone(); + let mut test_file_out = test_file.clone(); + test_file.push("test.txt"); + test_link.push("link.txt"); + + test_file_out.push("out"); + test_file_out.push("link.txt"); + fs_extra::dir::create_all(&test_file.parent().unwrap(), true).unwrap(); + + #[cfg(target_family = "unix")] + std::os::unix::fs::symlink(&test_file, &test_link).unwrap(); + + #[cfg(target_family = "windows")] + std::os::windows::fs::symlink(&test_file, &test_link).unwrap(); + + fs_extra::dir::create_all(&test_file_out.parent().unwrap(), true).unwrap(); + + write_all(&test_file, "test_data").unwrap(); + assert!(test_file.exists()); + assert!(!test_file_out.exists()); + let options = CopyOptions::new(); + copy(&test_link, &test_file_out, &options).unwrap(); + assert!(test_link.exists()); + assert!(test_file.exists()); + assert!(test_file_out.exists()); + assert_eq!(test_link.file_name(), test_file_out.file_name()); + assert!(files_eq(test_file, test_file_out).unwrap()); +} + #[test] fn it_copy_with_progress_work() { let mut test_file = PathBuf::from(TEST_FOLDER); @@ -567,6 +600,47 @@ fn it_copy_with_progress_exist_overwrite_and_skip_exist() { } } +#[test] +fn it_copy_with_progress_link_file() { + let mut test_file = std::fs::canonicalize(PathBuf::from(TEST_FOLDER)).unwrap(); + test_file.push("it_copy_with_progress_link_file"); + let mut test_link = test_file.clone(); + let mut test_file_out = test_file.clone(); + test_file.push("test.txt"); + test_link.push("link.txt"); + + test_file_out.push("out"); + test_file_out.push("link.txt"); + fs_extra::dir::create_all(&test_file.parent().unwrap(), true).unwrap(); + + #[cfg(target_family = "unix")] + std::os::unix::fs::symlink(&test_file, &test_link).unwrap(); + + #[cfg(target_family = "windows")] + std::os::windows::fs::symlink(&test_file, &test_link).unwrap(); + + fs_extra::dir::create_all(&test_file_out.parent().unwrap(), true).unwrap(); + + write_all(&test_file, "test_data").unwrap(); + assert!(test_file.exists()); + assert!(!test_file_out.exists()); + let func_test = |process_info: TransitProcess| { + println!("{}", process_info.total_bytes); + }; + let options = CopyOptions::new(); + + match copy_with_progress(&test_file, &test_file_out, &options, func_test) { + Ok(_) => { + assert!(test_file.exists()); + assert!(test_file_out.exists()); + assert_eq!(test_link.file_name(), test_file_out.file_name()); + assert!(files_eq(test_file, test_file_out).unwrap()); + () + } + Err(err) => panic!(err.to_string()), + } +} + #[test] fn it_move_work() { let mut test_file = PathBuf::from(TEST_FOLDER); @@ -768,6 +842,38 @@ fn it_move_exist_overwrite_and_skip_exist() { } } +#[test] +fn it_move_link_file() { + let mut test_file = std::fs::canonicalize(PathBuf::from(TEST_FOLDER)).unwrap(); + test_file.push("it_move_link_file"); + let mut test_link = test_file.clone(); + let mut test_file_out = test_file.clone(); + test_file.push("test.txt"); + test_link.push("link.txt"); + + test_file_out.push("out"); + test_file_out.push("link.txt"); + fs_extra::dir::create_all(&test_file.parent().unwrap(), true).unwrap(); + + #[cfg(target_family = "unix")] + std::os::unix::fs::symlink(&test_file, &test_link).unwrap(); + + #[cfg(target_family = "windows")] + std::os::windows::fs::symlink(&test_file, &test_link).unwrap(); + + fs_extra::dir::create_all(&test_file_out.parent().unwrap(), true).unwrap(); + + write_all(&test_file, "test_data").unwrap(); + assert!(test_file.exists()); + assert!(!test_file_out.exists()); + let options = CopyOptions::new(); + move_file(&test_link, &test_file_out, &options).unwrap(); + assert!(!test_link.exists()); + assert!(test_file.exists()); + assert!(test_file_out.exists()); + assert!(files_eq(test_file, test_file_out).unwrap()); +} + #[test] fn it_move_with_progress_work() { let mut test_file = PathBuf::from(TEST_FOLDER); @@ -1034,3 +1140,43 @@ fn it_move_with_progress_exist_overwrite_and_skip_exist() { Err(err) => panic!(err.to_string()), } } + +#[test] +fn it_move_with_progress_link_file() { + let mut test_file = std::fs::canonicalize(PathBuf::from(TEST_FOLDER)).unwrap(); + test_file.push("it_move_with_progress_link_file"); + let mut test_link = test_file.clone(); + let mut test_file_out = test_file.clone(); + test_file.push("test.txt"); + test_link.push("link.txt"); + + test_file_out.push("out"); + test_file_out.push("link.txt"); + fs_extra::dir::create_all(&test_file.parent().unwrap(), true).unwrap(); + + #[cfg(target_family = "unix")] + std::os::unix::fs::symlink(&test_file, &test_link).unwrap(); + + #[cfg(target_family = "windows")] + std::os::windows::fs::symlink(&test_file, &test_link).unwrap(); + + fs_extra::dir::create_all(&test_file_out.parent().unwrap(), true).unwrap(); + + write_all(&test_file, "test_data").unwrap(); + assert!(test_file.exists()); + assert!(!test_file_out.exists()); + let options = CopyOptions::new(); + let func_test = |process_info: TransitProcess| { + println!("{}", process_info.total_bytes); + }; + match move_file_with_progress(&test_link, &test_file_out, &options, func_test) { + Ok(_) => { + assert!(!test_link.exists()); + assert!(test_file.exists()); + assert!(test_file_out.exists()); + assert!(files_eq(test_file, test_file_out).unwrap()); + () + } + Err(err) => panic!(err.to_string()), + } +}