macro_rules! err { ($text:expr, $kind:expr) => { return Err(Error::new($kind, $text)) }; ($text:expr) => { err!($text, ErrorKind::Other) }; } /// The error type for fs_extra operations on files and directories. pub mod error; /// This module includes additional methods for working with files. /// /// One of the distinguishing features is receipt information /// about process work with files. /// /// # Example /// ```rust,ignore /// use std::path::Path; /// use std::{thread, time}; /// use std::sync::mpsc::{self, TryRecvError}; /// /// extern crate fs_extra; /// use fs_extra::file::*; /// use fs_extra::error::*; /// /// fn example_copy() -> Result<()> { /// let path_from = Path::new("./temp"); /// let path_to = path_from.join("out"); /// let test_file = (path_from.join("test_file.txt"), path_to.join("test_file.txt")); /// /// /// fs_extra::dir::create_all(&path_from, true)?; /// fs_extra::dir::create_all(&path_to, true)?; /// /// write_all(&test_file.0, "test_data")?; /// assert!(test_file.0.exists()); /// assert!(!test_file.1.exists()); /// /// /// let options = CopyOptions { /// buffer_size: 1, /// ..Default::default() /// } /// let (tx, rx) = mpsc::channel(); /// thread::spawn(move || { /// let handler = |process_info: TransitProcess| { /// tx.send(process_info).unwrap(); /// thread::sleep(time::Duration::from_millis(500)); /// }; /// copy_with_progress(&test_file.0, &test_file.1, &options, handler).unwrap(); /// assert!(test_file.0.exists()); /// assert!(test_file.1.exists()); /// /// }); /// loop { /// match rx.try_recv() { /// Ok(process_info) => { /// println!("{} of {} bytes", /// process_info.copied_bytes, /// process_info.total_bytes); /// } /// Err(TryRecvError::Disconnected) => { /// println!("finished"); /// break; /// } /// Err(TryRecvError::Empty) => {} /// } /// } /// Ok(()) /// /// } /// /// /// fn main() { /// example_copy(); /// } /// /// ``` pub mod file; /// This module includes additional methods for working with directories. /// /// One of the additional features is information /// about process and recursion operations. /// /// # Example /// ```rust,ignore /// use std::path::Path; /// use std::{thread, time}; /// use std::sync::mpsc::{self, TryRecvError}; /// /// extern crate fs_extra; /// use fs_extra::dir::*; /// use fs_extra::error::*; /// /// fn example_copy() -> Result<()> { /// /// let path_from = Path::new("./temp"); /// let path_to = path_from.join("out"); /// let test_folder = path_from.join("test_folder"); /// let dir = test_folder.join("dir"); /// let sub = dir.join("sub"); /// let file1 = dir.join("file1.txt"); /// let file2 = sub.join("file2.txt"); /// /// create_all(&sub, true)?; /// create_all(&path_to, true)?; /// fs_extra::file::write_all(&file1, "content1")?; /// fs_extra::file::write_all(&file2, "content2")?; /// /// assert!(dir.exists()); /// assert!(sub.exists()); /// assert!(file1.exists()); /// assert!(file2.exists()); /// /// /// let options = CopyOptions { /// buffer_size: 1, /// ..Default::default(), /// }; /// let (tx, rx) = mpsc::channel(); /// thread::spawn(move || { /// let handler = |process_info: TransitProcess| { /// tx.send(process_info).unwrap(); /// thread::sleep(time::Duration::from_millis(500)); /// }; /// copy_with_progress(&test_folder, &path_to, &options, handler).unwrap(); /// }); /// /// loop { /// match rx.try_recv() { /// Ok(process_info) => { /// println!("{} of {} bytes", /// process_info.copied_bytes, /// process_info.total_bytes); /// } /// Err(TryRecvError::Disconnected) => { /// println!("finished"); /// break; /// } /// Err(TryRecvError::Empty) => {} /// } /// } /// Ok(()) /// /// } /// fn main() { /// example_copy(); /// } /// ``` /// pub mod dir; use crate::error::*; use std::path::Path; /// Copies a list of directories and files to another place recursively. This function will /// also copy the permission bits of the original files to destination files (not for /// directories). /// /// # Errors /// /// This function will return an error in the following situations, but is not limited to just /// these case: /// /// * List `from` contains file or directory does not exist. /// /// * List `from` contains file or directory with invalid name. /// /// * The current process does not have the permission to access to file from `lists from` or /// `to`. /// /// # Example /// /// ```rust,ignore /// extern crate fs_extra; /// use fs_extra::dir::copy; /// /// let options = dir::CopyOptions::new(); //Initialize default values for CopyOptions /// /// // copy dir1 and file1.txt to target/dir1 and target/file1.txt /// let mut from_paths = Vec::new(); /// from_paths.push("source/dir1"); /// from_paths.push("source/file.txt"); /// copy_items(&from_paths, "target", &options)?; /// ``` /// pub fn copy_items(from: &[P], to: Q, options: &dir::CopyOptions) -> Result where P: AsRef, Q: AsRef, { let mut result: u64 = 0; if options.content_only { err!( "Options 'content_only' not acccess for copy_items function", ErrorKind::Other ); } for item in from { let item = item.as_ref(); if item.is_dir() { result += dir::copy(item, &to, options)?; } else if let Some(file_name) = item.file_name() { if let Some(file_name) = file_name.to_str() { let file_options = file::CopyOptions { overwrite: options.overwrite, skip_exist: options.skip_exist, ..Default::default() }; result += file::copy(item, to.as_ref().join(file_name), &file_options)?; } } else { err!("Invalid file name", ErrorKind::InvalidFileName); } } Ok(result) } /// A structure which includes information about the current status of copying or moving a directory. pub struct TransitProcess { /// Already copied bytes pub copied_bytes: u64, /// All the bytes which should be copied or moved (dir size). pub total_bytes: u64, /// Copied bytes on this time for file. pub file_bytes_copied: u64, /// Size of currently copied file. pub file_total_bytes: u64, /// Name of currently copied file. pub file_name: String, /// Name of currently copied folder. pub dir_name: String, /// Transit state pub state: dir::TransitState, } impl Clone for TransitProcess { fn clone(&self) -> TransitProcess { TransitProcess { copied_bytes: self.copied_bytes, total_bytes: self.total_bytes, file_bytes_copied: self.file_bytes_copied, file_total_bytes: self.file_total_bytes, file_name: self.file_name.clone(), dir_name: self.dir_name.clone(), state: self.state.clone(), } } } /// Copies a list of directories and files to another place recursively, with /// information about progress. This function will also copy the permission bits of the /// original files to destination files (not for directories). /// /// # Errors /// /// This function will return an error in the following situations, but is not limited to just /// these case: /// /// * List `from` contains file or directory does not exist. /// /// * List `from` contains file or directory with invalid name. /// /// * The current process does not have the permission to access to file from `lists from` or /// `to`. /// /// # Example /// ```rust,ignore /// /// extern crate fs_extra; /// use fs_extra::dir::copy; /// /// let options = dir::CopyOptions::new(); //Initialize default values for CopyOptions /// let handle = |process_info: TransitProcess| { /// println!("{}", process_info.total_bytes); /// fs_extra::dir::TransitProcessResult::ContinueOrAbort /// } /// // copy dir1 and file1.txt to target/dir1 and target/file1.txt /// let mut from_paths = Vec::new(); /// from_paths.push("source/dir1"); /// from_paths.push("source/file.txt"); /// copy_items_with_progress(&from_paths, "target", &options, handle)?; /// ``` /// pub fn copy_items_with_progress( from: &[P], to: Q, options: &dir::CopyOptions, mut progress_handler: F, ) -> Result where P: AsRef, Q: AsRef, F: FnMut(TransitProcess) -> dir::TransitProcessResult, { if options.content_only { err!( "Options 'content_only' not access for copy_items_with_progress function", ErrorKind::Other ); } let mut total_size = 0; let mut list_paths = Vec::new(); for item in from { let item = item.as_ref(); total_size += dir::get_size(item)?; list_paths.push(item); } let mut result: u64 = 0; let mut info_process = 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: dir::TransitState::Normal, }; let mut options = options.clone(); for item in list_paths { if item.is_dir() { if let Some(dir_name) = item.components().last() { if let Ok(dir_name) = dir_name.as_os_str().to_os_string().into_string() { info_process.dir_name = dir_name; } else { err!("Invalid folder from", ErrorKind::InvalidFolder); } } else { err!("Invalid folder from", ErrorKind::InvalidFolder); } let copied_bytes = result; let dir_options = options.clone(); let handler = |info: dir::TransitProcess| { info_process.copied_bytes = copied_bytes + info.copied_bytes; info_process.state = info.state; let result = progress_handler(info_process.clone()); match result { dir::TransitProcessResult::OverwriteAll => options.overwrite = true, dir::TransitProcessResult::SkipAll => options.skip_exist = true, _ => {} } result }; result += dir::copy_with_progress(item, &to, &dir_options, handler)?; } else { let mut file_options = file::CopyOptions { overwrite: options.overwrite, skip_exist: options.skip_exist, buffer_size: options.buffer_size, }; if let Some(file_name) = item.file_name() { if let Some(file_name) = file_name.to_str() { info_process.file_name = file_name.to_string(); } else { err!("Invalid file name", ErrorKind::InvalidFileName); } } else { err!("Invalid file name", ErrorKind::InvalidFileName); } info_process.file_bytes_copied = 0; info_process.file_total_bytes = item.metadata()?.len(); let copied_bytes = result; let file_name = to.as_ref().join(info_process.file_name.clone()); let mut work = true; let mut result_copy: Result; while work { { let handler = |info: file::TransitProcess| { info_process.copied_bytes = copied_bytes + info.copied_bytes; info_process.file_bytes_copied = info.copied_bytes; progress_handler(info_process.clone()); }; result_copy = file::copy_with_progress(item, &file_name, &file_options, handler); } match result_copy { Ok(val) => { result += val; work = false; } Err(err) => match err.kind { ErrorKind::AlreadyExists => { let mut info_process = info_process.clone(); info_process.state = dir::TransitState::Exists; let user_decide = progress_handler(info_process); match user_decide { dir::TransitProcessResult::Overwrite => { file_options.overwrite = true; } dir::TransitProcessResult::OverwriteAll => { file_options.overwrite = true; options.overwrite = true; } dir::TransitProcessResult::Skip => { file_options.skip_exist = true; } dir::TransitProcessResult::SkipAll => { file_options.skip_exist = true; options.skip_exist = true; } dir::TransitProcessResult::Retry => {} dir::TransitProcessResult::ContinueOrAbort => { let err_msg = err.to_string(); err!(err_msg.as_str(), err.kind) } dir::TransitProcessResult::Abort => { let err_msg = err.to_string(); err!(err_msg.as_str(), err.kind) } } } ErrorKind::PermissionDenied => { let mut info_process = info_process.clone(); info_process.state = dir::TransitState::Exists; let user_decide = progress_handler(info_process); match user_decide { dir::TransitProcessResult::Overwrite => { err!("Overwrite denied for this situation!", ErrorKind::Other); } dir::TransitProcessResult::OverwriteAll => { err!("Overwrite denied for this situation!", ErrorKind::Other); } dir::TransitProcessResult::Skip => { file_options.skip_exist = true; } dir::TransitProcessResult::SkipAll => { file_options.skip_exist = true; options.skip_exist = true; } dir::TransitProcessResult::Retry => {} dir::TransitProcessResult::ContinueOrAbort => { let err_msg = err.to_string(); err!(err_msg.as_str(), err.kind) } dir::TransitProcessResult::Abort => { let err_msg = err.to_string(); err!(err_msg.as_str(), err.kind) } } } _ => { let err_msg = err.to_string(); err!(err_msg.as_str(), err.kind) } }, } } } } Ok(result) } /// Moves a list of directories and files to another place recursively. This function will /// also copy the permission bits of the original files to destination files (not for /// directories). /// /// # Errors /// /// This function will return an error in the following situations, but is not limited to just /// these case: /// /// * List `from` contains file or directory does not exist. /// /// * List `from` contains file or directory with invalid name. /// /// * The current process does not have the permission to access to file from `lists from` or /// `to`. /// /// # Example /// /// ```rust,ignore /// extern crate fs_extra; /// use fs_extra::dir::copy; /// /// let options = dir::CopyOptions::new(); //Initialize default values for CopyOptions /// /// // move dir1 and file1.txt to target/dir1 and target/file1.txt /// let mut from_paths = Vec::new(); /// from_paths.push("source/dir1"); /// from_paths.push("source/file.txt"); /// move_items(&from_paths, "target", &options)?; /// ``` /// pub fn move_items(from_items: &[P], to: Q, options: &dir::CopyOptions) -> Result where P: AsRef, Q: AsRef, { if options.content_only { err!( "Options 'content_only' not access for move_items function", ErrorKind::Other ); } let mut total_size = 0; let mut list_paths = Vec::new(); for item in from_items { let item = item.as_ref(); total_size += dir::get_size(item)?; list_paths.push(item); } let mut result = 0; let mut info_process = 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: dir::TransitState::Normal, }; for item in list_paths { if item.is_dir() { if let Some(dir_name) = item.components().last() { if let Ok(dir_name) = dir_name.as_os_str().to_os_string().into_string() { info_process.dir_name = dir_name; } else { err!("Invalid folder from", ErrorKind::InvalidFolder); } } else { err!("Invalid folder from", ErrorKind::InvalidFolder); } result += dir::move_dir(item, &to, options)?; } else { let file_options = file::CopyOptions { overwrite: options.overwrite, skip_exist: options.skip_exist, buffer_size: options.buffer_size, }; if let Some(file_name) = item.file_name() { if let Some(file_name) = file_name.to_str() { info_process.file_name = file_name.to_string(); } else { err!("Invalid file name", ErrorKind::InvalidFileName); } } else { err!("Invalid file name", ErrorKind::InvalidFileName); } info_process.file_bytes_copied = 0; info_process.file_total_bytes = item.metadata()?.len(); let file_name = to.as_ref().join(info_process.file_name.clone()); result += file::move_file(item, &file_name, &file_options)?; } } Ok(result) } /// Moves a list of directories and files to another place recursively, with /// information about progress. This function will also copy the permission bits of the /// original files to destination files (not for directories). /// /// # Errors /// /// This function will return an error in the following situations, but is not limited to just /// these case: /// /// * List `from` contains file or directory does not exist. /// /// * List `from` contains file or directory with invalid name. /// /// * The current process does not have the permission to access to file from `lists from` or /// `to`. /// /// # Example /// /// ```rust,ignore /// extern crate fs_extra; /// use fs_extra::dir::copy; /// /// let options = dir::CopyOptions::new(); //Initialize default values for CopyOptions /// let handle = |process_info: TransitProcess| { /// println!("{}", process_info.total_bytes); /// fs_extra::dir::TransitProcessResult::ContinueOrAbort /// } /// // move dir1 and file1.txt to target/dir1 and target/file1.txt /// let mut from_paths = Vec::new(); /// from_paths.push("source/dir1"); /// from_paths.push("source/file.txt"); /// move_items_with_progress(&from_paths, "target", &options, handle)?; /// ``` /// pub fn move_items_with_progress( from_items: &[P], to: Q, options: &dir::CopyOptions, mut progress_handler: F, ) -> Result where P: AsRef, Q: AsRef, F: FnMut(TransitProcess) -> dir::TransitProcessResult, { if options.content_only { err!( "Options 'content_only' not access for move_items_with_progress function", ErrorKind::Other ); } let mut total_size = 0; let mut list_paths = Vec::new(); for item in from_items { let item = item.as_ref(); total_size += dir::get_size(item)?; list_paths.push(item); } let mut result = 0; let mut info_process = 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: dir::TransitState::Normal, }; let mut options = options.clone(); for item in list_paths { if item.is_dir() { if let Some(dir_name) = item.components().last() { if let Ok(dir_name) = dir_name.as_os_str().to_os_string().into_string() { info_process.dir_name = dir_name; } else { err!("Invalid folder from", ErrorKind::InvalidFolder); } } else { err!("Invalid folder from", ErrorKind::InvalidFolder); } let copied_bytes = result; let dir_options = options.clone(); let handler = |info: dir::TransitProcess| { info_process.copied_bytes = copied_bytes + info.copied_bytes; info_process.state = info.state; let result = progress_handler(info_process.clone()); match result { dir::TransitProcessResult::OverwriteAll => options.overwrite = true, dir::TransitProcessResult::SkipAll => options.skip_exist = true, _ => {} } result }; result += dir::move_dir_with_progress(item, &to, &dir_options, handler)?; } else { let mut file_options = file::CopyOptions { overwrite: options.overwrite, skip_exist: options.skip_exist, buffer_size: options.buffer_size, }; if let Some(file_name) = item.file_name() { if let Some(file_name) = file_name.to_str() { info_process.file_name = file_name.to_string(); } else { err!("Invalid file name", ErrorKind::InvalidFileName); } } else { err!("Invalid file name", ErrorKind::InvalidFileName); } info_process.file_bytes_copied = 0; info_process.file_total_bytes = item.metadata()?.len(); let copied_bytes = result; let file_name = to.as_ref().join(info_process.file_name.clone()); let mut work = true; let mut result_copy: Result; while work { { let handler = |info: file::TransitProcess| { info_process.copied_bytes = copied_bytes + info.copied_bytes; info_process.file_bytes_copied = info.copied_bytes; progress_handler(info_process.clone()); }; result_copy = file::move_file_with_progress(item, &file_name, &file_options, handler); } match result_copy { Ok(val) => { result += val; work = false; } Err(err) => match err.kind { ErrorKind::AlreadyExists => { let mut info_process = info_process.clone(); info_process.state = dir::TransitState::Exists; let user_decide = progress_handler(info_process); match user_decide { dir::TransitProcessResult::Overwrite => { file_options.overwrite = true; } dir::TransitProcessResult::OverwriteAll => { file_options.overwrite = true; options.overwrite = true; } dir::TransitProcessResult::Skip => { file_options.skip_exist = true; } dir::TransitProcessResult::SkipAll => { file_options.skip_exist = true; options.skip_exist = true; } dir::TransitProcessResult::Retry => {} dir::TransitProcessResult::ContinueOrAbort => { let err_msg = err.to_string(); err!(err_msg.as_str(), err.kind) } dir::TransitProcessResult::Abort => { let err_msg = err.to_string(); err!(err_msg.as_str(), err.kind) } } } ErrorKind::PermissionDenied => { let mut info_process = info_process.clone(); info_process.state = dir::TransitState::Exists; let user_decide = progress_handler(info_process); match user_decide { dir::TransitProcessResult::Overwrite => { err!("Overwrite denied for this situation!", ErrorKind::Other); } dir::TransitProcessResult::OverwriteAll => { err!("Overwrite denied for this situation!", ErrorKind::Other); } dir::TransitProcessResult::Skip => { file_options.skip_exist = true; } dir::TransitProcessResult::SkipAll => { file_options.skip_exist = true; options.skip_exist = true; } dir::TransitProcessResult::Retry => {} dir::TransitProcessResult::ContinueOrAbort => { let err_msg = err.to_string(); err!(err_msg.as_str(), err.kind) } dir::TransitProcessResult::Abort => { let err_msg = err.to_string(); err!(err_msg.as_str(), err.kind) } } } _ => { let err_msg = err.to_string(); err!(err_msg.as_str(), err.kind) } }, } } } } Ok(result) } /// Removes a list of files or directories. /// /// # Example /// /// ```rust,ignore /// let mut from_paths = Vec::new(); /// from_paths.push("source/dir1"); /// from_paths.push("source/file.txt"); /// /// remove_items(&from_paths).unwrap(); /// ``` /// pub fn remove_items

(from_items: &[P]) -> Result<()> where P: AsRef, { for item in from_items { let item = item.as_ref(); if item.is_dir() { dir::remove(item)?; } else { file::remove(item)? } } Ok(()) }