use crate::error::*; use std::collections::{HashMap, HashSet}; use std::fs::{create_dir, create_dir_all, read_dir, remove_dir_all, Metadata}; use std::path::{Path, PathBuf}; use std::time::SystemTime; /// Options and flags which can be used to configure how a file will be copied or moved. #[derive(Clone)] pub struct CopyOptions { /// Overwrite existing files if true (default: false). pub overwrite: bool, /// Skip existing files if true (default: false). pub skip_exist: bool, /// Buffer size that specifies the amount of bytes to be moved or copied before the progress handler is called. This only affects functions with progress handlers. (default: 64000) pub buffer_size: usize, /// Recursively copy a directory with a new name or place it inside the destination (default: false, same behaviors as cp -r on Unix) pub copy_inside: bool, /// Copy only contents without a creating a new folder in the destination folder (default: false). pub content_only: bool, /// Sets levels reading. Set 0 for read all directory folder (default: 0). /// /// Warning: Work only for copy operations! pub depth: u64, } impl CopyOptions { /// Initialize struct CopyOptions with default value. /// /// ```rust,ignore /// overwrite: false /// /// skip_exist: false /// /// buffer_size: 64000 // 64kb /// /// copy_inside: false /// ``` pub fn new() -> CopyOptions { CopyOptions { overwrite: false, skip_exist: false, buffer_size: 64000, // 64kb copy_inside: false, content_only: false, depth: 0, } } /// Overwrite existing files if true. pub fn overwrite(mut self, overwrite: bool) -> Self { self.overwrite = overwrite; self } /// Skip existing files if true. pub fn skip_exist(mut self, skip_exist: bool) -> Self { self.skip_exist = skip_exist; self } /// Buffer size that specifies the amount of bytes to be moved or copied before the progress handler is called. This only affects functions with progress handlers. pub fn buffer_size(mut self, buffer_size: usize) -> Self { self.buffer_size = buffer_size; self } /// Recursively copy a directory with a new name or place it inside the destination (default: false, same behaviors as cp -r on Unix) pub fn copy_inside(mut self, copy_inside: bool) -> Self { self.copy_inside = copy_inside; self } /// Copy only contents without a creating a new folder in the destination folder. pub fn content_only(mut self, content_only: bool) -> Self { self.content_only = content_only; self } /// Sets levels reading. Set 0 for read all directory folder pub fn depth(mut self, depth: u64) -> Self { self.depth = depth; self } } impl Default for CopyOptions { fn default() -> Self { CopyOptions::new() } } // Options and flags which can be used to configure how to read a directory. #[derive(Clone, Default)] pub struct DirOptions { /// Sets levels reading. Set value 0 for read all directory folder. By default 0. pub depth: u64, } impl DirOptions { /// Initialize struct DirOptions with default value. pub fn new() -> DirOptions { Default::default() } } /// A structure which include information about directory pub struct DirContent { /// Directory size in bytes. pub dir_size: u64, /// List all files directory and sub directories. pub files: Vec, /// List all folders and sub folders directory. pub directories: Vec, } /// A structure which include information about the current status of the copy or move directory. pub struct TransitProcess { /// Copied bytes on this time for folder pub copied_bytes: u64, /// All the bytes which should to copy or move (dir size). pub total_bytes: u64, /// Copied bytes on this time for file. pub file_bytes_copied: u64, /// Size current copied file. pub file_total_bytes: u64, /// Name current copied file. pub file_name: String, /// Transit state pub state: TransitState, } /// #[derive(Hash, Eq, PartialEq, Clone)] pub enum TransitState { /// Standard state. Normal, /// Pause state when destination path exists. Exists, /// Pause state when current process does not have the permission to access from or to /// path. NoAccess, } /// Available returns codes for user decide pub enum TransitProcessResult { /// Rewrite exist file or directory. Overwrite, /// Rewrite for all exist files or directories. OverwriteAll, /// Skip current problem file or directory. Skip, /// Skip for all problems file or directory. SkipAll, /// Retry current operation. Retry, /// Abort current operation. Abort, /// Continue execute process if process not have error and abort if process content error. ContinueOrAbort, } 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(), state: self.state.clone(), } } } /// Available attributes for get information about directory entry. #[derive(Hash, Eq, PartialEq, Clone)] pub enum DirEntryAttr { /// Folder name or file name without extension. Name, /// File extension. Ext, /// Folder name or file name with extension. FullName, /// Path to file or directory. Path, /// Dos path to file or directory. DosPath, /// File size in bytes. FileSize, /// Size file or directory in bytes. /// /// `Attention!`: This operation very expensive and sometimes required additional rights. Size, /// Return whether entry is directory or not. IsDir, /// Return whether entry is file or not. IsFile, /// Last modification time for directory entry. Modified, /// Last access time for directory entry. Accessed, /// Created time for directory entry. /// /// `Attention!`: Not supported UNIX platform. Created, /// Return or not return base information target folder. BaseInfo, } /// Available types for directory entry. pub enum DirEntryValue { /// String type String(String), /// Boolean type Boolean(bool), /// SystemTime type SystemTime(SystemTime), /// u64 type U64(u64), } /// Result returned by the `ls` function. pub struct LsResult { /// Base folder target path pub base: HashMap, /// Collection directory entry with information. pub items: Vec>, } /// Returned information about directory entry with information which you choose in config. /// /// This function takes to arguments: /// /// * `path` - Path to directory. /// /// * `config` - Set attributes which you want see inside return data. /// /// # Errors /// /// This function will return an error in the following situations, but is not limited to just /// these cases: /// /// * This `path` does not exist. /// * Invalid `path`. /// * The current process does not have the permission to access `path`. /// /// #Examples /// /// ```rust,ignore /// extern crate fs_extra; /// use fs_extra::dir::{get_details_entry, DirEntryAttr}; /// use std::collections::{HashMap, HashSet}; /// /// let mut config = HashSet::new(); /// config.insert(DirEntryAttr::Name); /// config.insert(DirEntryAttr::Size); /// /// let entry_info = get_details_entry("test", &config); /// assert_eq!(2, entry_info.len()); /// ``` pub fn get_details_entry

( path: P, config: &HashSet, ) -> Result> where P: AsRef, { let path = path.as_ref(); let metadata = path.metadata()?; get_details_entry_with_meta(path, config, metadata) } fn get_details_entry_with_meta

( path: P, config: &HashSet, metadata: Metadata, ) -> Result> where P: AsRef, { let path = path.as_ref(); let mut item = HashMap::new(); if config.contains(&DirEntryAttr::Name) { if metadata.is_dir() { if let Some(file_name) = path.file_name() { item.insert( DirEntryAttr::Name, DirEntryValue::String(file_name.to_os_string().into_string()?), ); } else { item.insert(DirEntryAttr::Name, DirEntryValue::String(String::new())); } } else if let Some(file_stem) = path.file_stem() { item.insert( DirEntryAttr::Name, DirEntryValue::String(file_stem.to_os_string().into_string()?), ); } else { item.insert(DirEntryAttr::Name, DirEntryValue::String(String::new())); } } if config.contains(&DirEntryAttr::Ext) { if let Some(value) = path.extension() { item.insert( DirEntryAttr::Ext, DirEntryValue::String(value.to_os_string().into_string()?), ); } else { item.insert(DirEntryAttr::Ext, DirEntryValue::String(String::from(""))); } } if config.contains(&DirEntryAttr::FullName) { if let Some(file_name) = path.file_name() { item.insert( DirEntryAttr::FullName, DirEntryValue::String(file_name.to_os_string().into_string()?), ); } else { item.insert(DirEntryAttr::FullName, DirEntryValue::String(String::new())); } } if config.contains(&DirEntryAttr::Path) { let mut result_path: PathBuf; match path.canonicalize() { Ok(new_path) => { result_path = new_path; } Err(_) => { if let Some(parent_path) = path.parent() { if let Some(name) = path.file_name() { result_path = parent_path.canonicalize()?; result_path.push(name); } else { err!("Error get part name path", ErrorKind::Other); } } else { err!("Error get parent path", ErrorKind::Other); } } } let mut path = result_path.as_os_str().to_os_string().into_string()?; if path.find("\\\\?\\") == Some(0) { path = path[4..].to_string(); } item.insert(DirEntryAttr::Path, DirEntryValue::String(path)); } if config.contains(&DirEntryAttr::DosPath) { let mut result_path: PathBuf; match path.canonicalize() { Ok(new_path) => { result_path = new_path; } Err(_) => { if let Some(parent_path) = path.parent() { if let Some(name) = path.file_name() { result_path = parent_path.canonicalize()?; result_path.push(name); } else { err!("Error get part name path", ErrorKind::Other); } } else { err!("Error get parent path", ErrorKind::Other); } } } let path = result_path.as_os_str().to_os_string().into_string()?; item.insert(DirEntryAttr::DosPath, DirEntryValue::String(path)); } if config.contains(&DirEntryAttr::Size) { item.insert(DirEntryAttr::Size, DirEntryValue::U64(get_size(&path)?)); } if config.contains(&DirEntryAttr::FileSize) { item.insert(DirEntryAttr::FileSize, DirEntryValue::U64(metadata.len())); } if config.contains(&DirEntryAttr::IsDir) { item.insert( DirEntryAttr::IsDir, DirEntryValue::Boolean(metadata.is_dir()), ); } if config.contains(&DirEntryAttr::IsFile) { item.insert( DirEntryAttr::IsFile, DirEntryValue::Boolean(metadata.is_file()), ); } if config.contains(&DirEntryAttr::Modified) { item.insert( DirEntryAttr::Modified, DirEntryValue::SystemTime(metadata.modified()?), ); } if config.contains(&DirEntryAttr::Accessed) { item.insert( DirEntryAttr::Accessed, DirEntryValue::SystemTime(metadata.accessed()?), ); } if config.contains(&DirEntryAttr::Created) { item.insert( DirEntryAttr::Created, DirEntryValue::SystemTime(metadata.created()?), ); } Ok(item) } /// Returns a collection of directory entries with attributes specifying the information that should be returned. /// /// This function takes to arguments: /// /// * `path` - Path to directory. /// /// * `config` - Set attributes which you want see in return data. /// /// # Errors /// /// This function will return an error in the following situations, but is not limited to just /// these cases: /// /// * This `path` directory does not exist. /// * Invalid `path`. /// * The current process does not have the permission to access `path`. /// /// #Examples /// /// ```rust,ignore /// extern crate fs_extra; /// use fs_extra::dir::{ls, DirEntryAttr, LsResult}; /// use std::collections::HashSet; /// /// let mut config = HashSet::new(); /// config.insert(DirEntryAttr::Name); /// config.insert(DirEntryAttr::Size); /// config.insert(DirEntryAttr::BaseInfo); /// /// let result = ls("test", &config); /// assert_eq!(2, ls_result.items.len()); /// assert_eq!(2, ls_result.base.len()); /// ``` pub fn ls

(path: P, config: &HashSet) -> Result where P: AsRef, { let mut items = Vec::new(); let path = path.as_ref(); if !path.is_dir() { err!("Path does not directory", ErrorKind::InvalidFolder); } for entry in read_dir(&path)? { let entry = entry?; let path = entry.path(); let metadata = entry.metadata()?; let item = get_details_entry_with_meta(path, &config, metadata)?; items.push(item); } let mut base = HashMap::new(); if config.contains(&DirEntryAttr::BaseInfo) { base = get_details_entry(&path, &config)?; } Ok(LsResult { items, base }) } /// Creates a new, empty directory at the provided path. /// /// This function takes to arguments: /// /// * `path` - Path to new directory. /// /// * `erase` - If set true and folder exist, then folder will be erased. /// /// #Errors /// /// This function will return an error in the following situations, /// but is not limited to just these cases: /// /// * User lacks permissions to create directory at `path`. /// /// * `path` already exists if `erase` set false. /// /// #Examples /// /// ```rust,ignore /// extern crate fs_extra; /// use fs_extra::dir::create; /// /// create("dir", false); // create directory /// ``` pub fn create

(path: P, erase: bool) -> Result<()> where P: AsRef, { if erase && path.as_ref().exists() { remove(&path)?; } Ok(create_dir(&path)?) } /// Recursively create a directory and all of its parent components if they are missing. /// /// This function takes to arguments: /// /// * `path` - Path to new directory. /// /// * `erase` - If set true and folder exist, then folder will be erased. /// ///#Errors /// /// This function will return an error in the following situations, /// but is not limited to just these cases: /// /// * User lacks permissions to create directory at `path`. /// /// * `path` already exists if `erase` set false. /// /// #Examples /// /// ```rust,ignore /// extern crate fs_extra; /// use fs_extra::dir::create_all; /// /// create_all("/some/dir", false); // create directory some and dir pub fn create_all

(path: P, erase: bool) -> Result<()> where P: AsRef, { if erase && path.as_ref().exists() { remove(&path)?; } Ok(create_dir_all(&path)?) } /// Copies the directory contents from one place to another using recursive method. /// 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 cases: /// /// * This `from` path is not a directory. /// * This `from` directory does not exist. /// * Invalid folder name for `from` or `to`. /// * The current process does not have the permission to access `from` or write `to`. /// /// # Example /// ```rust,ignore /// extern crate fs_extra; /// use fs_extra::dir::copy; /// /// let options = CopyOptions::new(); //Initialize default values for CopyOptions /// // options.mirror_copy = true; // To mirror copy the whole structure of the source directory /// /// /// // copy source/dir1 to target/dir1 /// copy("source/dir1", "target/dir1", &options)?; /// /// ``` pub fn copy(from: P, to: Q, options: &CopyOptions) -> Result where P: AsRef, Q: AsRef, { let from = from.as_ref(); if !from.exists() { if let Some(msg) = from.to_str() { let msg = format!("Path \"{}\" does not exist or you don't have access!", msg); err!(&msg, ErrorKind::NotFound); } err!( "Path does not exist Or you don't have access!", ErrorKind::NotFound ); } if !from.is_dir() { if let Some(msg) = from.to_str() { let msg = format!("Path \"{}\" is not a directory!", msg); err!(&msg, ErrorKind::InvalidFolder); } err!("Path is not a directory!", ErrorKind::InvalidFolder); } let dir_name; if let Some(val) = from.components().last() { dir_name = val.as_os_str(); } else { err!("Invalid folder from", ErrorKind::InvalidFolder); } let mut to: PathBuf = to.as_ref().to_path_buf(); if (to.exists() || !options.copy_inside) && !options.content_only { to.push(dir_name); } let mut read_options = DirOptions::new(); if options.depth > 0 { read_options.depth = options.depth; } let dir_content = get_dir_content2(from, &read_options)?; for directory in dir_content.directories { let tmp_to = Path::new(&directory).strip_prefix(from)?; let dir = to.join(&tmp_to); if !dir.exists() { if options.copy_inside { create_all(dir, false)?; } else { create(dir, false)?; } } } let mut result: u64 = 0; for file in dir_content.files { let to = to.to_path_buf(); let tp = Path::new(&file).strip_prefix(from)?; let path = to.join(&tp); let file_options = super::file::CopyOptions { overwrite: options.overwrite, 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) } } } } Ok(result) } /// Return DirContent which contains information about directory: /// /// * Size of the directory in bytes. /// * List of source paths of files in the directory (files inside subdirectories included too). /// * List of source paths of all directories and subdirectories. /// /// # Errors /// /// This function will return an error in the following situations, but is not limited to just /// these cases: /// /// * This `path` directory does not exist. /// * Invalid `path`. /// * The current process does not have the permission to access `path`. /// /// # Examples /// ```rust,ignore /// extern crate fs_extra; /// use fs_extra::dir::get_dir_content; /// /// let dir_content = get_dir_content("dir")?; /// for directory in dir_content.directories { /// println!("{}", directory); // print directory path /// } /// ``` /// pub fn get_dir_content

(path: P) -> Result where P: AsRef, { let options = DirOptions::new(); get_dir_content2(path, &options) } /// Return DirContent which contains information about directory: /// /// * Size directory. /// * List all files source directory(files subdirectories included too). /// * List all directory and subdirectories source path. /// /// # Errors /// /// This function will return an error in the following situations, but is not limited to just /// these cases: /// /// * This `path` directory does not exist. /// * Invalid `path`. /// * The current process does not have the permission to access `path`. /// /// # Examples /// ```rust,ignore /// extern crate fs_extra; /// use fs_extra::dir::{DirOptions, get_dir_content2}; /// /// let mut options = DirOptions::new(); /// options.depth = 3; // Get 3 levels of folder. /// let dir_content = get_dir_content2("dir", &options)?; /// for directory in dir_content.directories { /// println!("{}", directory); // print directory path /// } /// ``` /// pub fn get_dir_content2

(path: P, options: &DirOptions) -> Result where P: AsRef, { let mut depth = 0; if options.depth != 0 { depth = options.depth + 1; } _get_dir_content(path, depth) } fn _get_dir_content

(path: P, mut depth: u64) -> Result where P: AsRef, { let mut directories = Vec::new(); let mut files = Vec::new(); let mut dir_size; let item = path.as_ref().to_str(); if item.is_none() { err!("Invalid path", ErrorKind::InvalidPath); } let item = item.unwrap().to_string(); if path.as_ref().is_dir() { dir_size = path.as_ref().metadata()?.len(); directories.push(item); if depth == 0 || depth > 1 { if depth > 1 { depth -= 1; } for entry in read_dir(&path)? { let _path = entry?.path(); match _get_dir_content(_path, depth) { Ok(items) => { let mut _files = items.files; let mut _directories = items.directories; dir_size += items.dir_size; files.append(&mut _files); directories.append(&mut _directories); } Err(err) => return Err(err), } } } } else { dir_size = path.as_ref().metadata()?.len(); files.push(item); } Ok(DirContent { dir_size, files, directories, }) } /// Returns the size of the file or directory in bytes.(!important: folders size not count) /// /// If used on a directory, this function will recursively iterate over every file and every /// directory inside the directory. This can be very time consuming if used on large directories. /// /// Does not follow symlinks. /// /// # Errors /// /// This function will return an error in the following situations, but is not limited to just /// these cases: /// /// * This `path` directory does not exist. /// * Invalid `path`. /// * The current process does not have the permission to access `path`. /// /// # Examples /// ```rust,ignore /// extern crate fs_extra; /// use fs_extra::dir::get_size; /// /// let folder_size = get_size("dir")?; /// println!("{}", folder_size); // print directory size in bytes /// ``` pub fn get_size

(path: P) -> Result where P: AsRef, { // Using `fs::symlink_metadata` since we don't want to follow symlinks, // as we're calculating the exact size of the requested path itself. let path_metadata = path.as_ref().symlink_metadata()?; let mut size_in_bytes = 0; if path_metadata.is_dir() { for entry in read_dir(&path)? { let entry = entry?; // `DirEntry::metadata` does not follow symlinks (unlike `fs::metadata`), so in the // case of symlinks, this is the size of the symlink itself, not its target. let entry_metadata = entry.metadata()?; if entry_metadata.is_dir() { // The size of the directory entry itself will be counted inside the `get_size()` call, // so we intentionally don't also add `entry_metadata.len()` to the total here. size_in_bytes += get_size(entry.path())?; } else { size_in_bytes += entry_metadata.len(); } } } else { size_in_bytes = path_metadata.len(); } Ok(size_in_bytes) } /// Copies the directory contents from one place to another using recursive method, /// 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 cases: /// /// * This `from` path is not a directory. /// * This `from` directory does not exist. /// * Invalid folder name for `from` or `to`. /// * The current process does not have the permission to access `from` or write `to`. /// /// # Example /// ```rust,ignore /// extern crate fs_extra; /// use fs_extra::dir::copy; /// /// let options = CopyOptions::new(); //Initialize default values for CopyOptions /// let handle = |process_info: TransitProcess| { /// println!("{}", process_info.total_bytes); /// fs_extra::dir::TransitProcessResult::ContinueOrAbort /// } /// // copy source/dir1 to target/dir1 /// copy_with_progress("source/dir1", "target/dir1", &options, handle)?; /// /// ``` pub fn copy_with_progress( from: P, to: Q, options: &CopyOptions, mut progress_handler: F, ) -> Result where P: AsRef, Q: AsRef, F: FnMut(TransitProcess) -> TransitProcessResult, { let from = from.as_ref(); if !from.exists() { if let Some(msg) = from.to_str() { let msg = format!("Path \"{}\" does not exist or you don't have access!", msg); err!(&msg, ErrorKind::NotFound); } err!( "Path does not exist or you don't have access!", ErrorKind::NotFound ); } let mut to: PathBuf = to.as_ref().to_path_buf(); if !from.is_dir() { if let Some(msg) = from.to_str() { let msg = format!("Path \"{}\" is not a directory!", msg); err!(&msg, ErrorKind::InvalidFolder); } err!("Path is not a directory!", ErrorKind::InvalidFolder); } let dir_name; if let Some(val) = from.components().last() { dir_name = val.as_os_str(); } else { err!("Invalid folder from", ErrorKind::InvalidFolder); } if (to.exists() || !options.copy_inside) && !options.content_only { to.push(dir_name); } let mut read_options = DirOptions::new(); if options.depth > 0 { read_options.depth = options.depth; } let dir_content = get_dir_content2(from, &read_options)?; for directory in dir_content.directories { let tmp_to = Path::new(&directory).strip_prefix(from)?; let dir = to.join(&tmp_to); if !dir.exists() { if options.copy_inside { create_all(dir, false)?; } else { create(dir, false)?; } } } let mut result: u64 = 0; let mut info_process = TransitProcess { copied_bytes: 0, total_bytes: dir_content.dir_size, file_bytes_copied: 0, file_total_bytes: 0, file_name: String::new(), state: TransitState::Normal, }; let mut options = options.clone(); for file in dir_content.files { let mut to = to.to_path_buf(); let tp = Path::new(&file).strip_prefix(from)?; let path = to.join(&tp); let file_name = path.file_name(); if file_name.is_none() { err!("No file name"); } let file_name = file_name.unwrap(); to.push(file_name); let mut file_options = super::file::CopyOptions { overwrite: options.overwrite, skip_exist: options.skip_exist, buffer_size: options.buffer_size, }; if let Some(file_name) = file_name.to_str() { info_process.file_name = file_name.to_string(); } else { err!("Invalid file name", ErrorKind::InvalidFileName); } info_process.file_bytes_copied = 0; info_process.file_total_bytes = Path::new(&file).metadata()?.len(); let mut result_copy: Result; let mut work = true; let copied_bytes = result; while work { { let _progress_handler = |info: super::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 = super::file::copy_with_progress(&file, &path, &file_options, _progress_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 = TransitState::Exists; let user_decide = progress_handler(info_process); match user_decide { TransitProcessResult::Overwrite => { file_options.overwrite = true; } TransitProcessResult::OverwriteAll => { file_options.overwrite = true; options.overwrite = true; } TransitProcessResult::Skip => { file_options.skip_exist = true; } TransitProcessResult::SkipAll => { file_options.skip_exist = true; options.skip_exist = true; } TransitProcessResult::Retry => {} TransitProcessResult::ContinueOrAbort => { let err_msg = err.to_string(); err!(err_msg.as_str(), err.kind) } 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 = TransitState::Exists; let user_decide = progress_handler(info_process); match user_decide { TransitProcessResult::Overwrite => { err!("Overwrite denied for this situation!", ErrorKind::Other); } TransitProcessResult::OverwriteAll => { err!("Overwrite denied for this situation!", ErrorKind::Other); } TransitProcessResult::Skip => { file_options.skip_exist = true; } TransitProcessResult::SkipAll => { file_options.skip_exist = true; options.skip_exist = true; } TransitProcessResult::Retry => {} TransitProcessResult::ContinueOrAbort => { let err_msg = err.to_string(); err!(err_msg.as_str(), err.kind) } 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 the directory contents from one place to another. /// 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 cases: /// /// * This `from` path is not a directory. /// * This `from` directory does not exist. /// * Invalid folder name for `from` or `to`. /// * The current process does not have the permission to access `from` or write `to`. /// /// # Example /// ```rust,ignore /// extern crate fs_extra; /// use fs_extra::dir::move_dir; /// /// let options = CopyOptions::new(); //Initialize default values for CopyOptions /// /// // move source/dir1 to target/dir1 /// move_dir("source/dir1", "target/dir1", &options)?; /// /// ``` pub fn move_dir(from: P, to: Q, options: &CopyOptions) -> Result where P: AsRef, Q: AsRef, { let mut is_remove = true; if options.skip_exist && to.as_ref().exists() && !options.overwrite { is_remove = false; } let from = from.as_ref(); if !from.exists() { if let Some(msg) = from.to_str() { let msg = format!("Path \"{}\" does not exist", msg); err!(&msg, ErrorKind::NotFound); } err!( "Path does not exist or you don't have access!", ErrorKind::NotFound ); } let mut to: PathBuf = to.as_ref().to_path_buf(); if !from.is_dir() { if let Some(msg) = from.to_str() { let msg = format!( "Path \"{}\" is not a directory or you don't have access!", msg ); err!(&msg, ErrorKind::InvalidFolder); } err!( "Path is not a directory or you don't have access!", ErrorKind::InvalidFolder ); } let dir_name; if let Some(val) = from.components().last() { dir_name = val.as_os_str(); } else { err!("Invalid folder from", ErrorKind::InvalidFolder); } if (to.exists() || !options.copy_inside) && !options.content_only { to.push(dir_name); } let dir_content = get_dir_content(from)?; for directory in dir_content.directories { let tmp_to = Path::new(&directory).strip_prefix(from)?; let dir = to.join(&tmp_to); if !dir.exists() { if options.copy_inside { create_all(dir, false)?; } else { create(dir, false)?; } } } let mut result: u64 = 0; for file in dir_content.files { let to = to.to_path_buf(); let tp = Path::new(&file).strip_prefix(from)?; let path = to.join(&tp); let file_options = super::file::CopyOptions { overwrite: options.overwrite, 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::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_remove { remove(from)?; } Ok(result) } /// Moves the directory contents from one place to another 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 cases: /// /// * This `from` path is not a directory. /// * This `from` directory does not exist. /// * Invalid folder name for `from` or `to`. /// * The current process does not have the permission to access `from` or write `to`. /// /// # Example /// ```rust,ignore /// extern crate fs_extra; /// use fs_extra::dir::move_dir_with_progress; /// /// let options = CopyOptions::new(); //Initialize default values for CopyOptions /// let handle = |process_info: TransitProcess| { /// println!("{}", process_info.total_bytes); /// fs_extra::dir::TransitProcessResult::ContinueOrAbort /// } /// /// // move source/dir1 to target/dir1 /// move_dir_with_progress("source/dir1", "target/dir1", &options, handle)?; /// /// ``` pub fn move_dir_with_progress( from: P, to: Q, options: &CopyOptions, mut progress_handler: F, ) -> Result where P: AsRef, Q: AsRef, F: FnMut(TransitProcess) -> TransitProcessResult, { let mut is_remove = true; if options.skip_exist && to.as_ref().exists() && !options.overwrite { is_remove = false; } let from = from.as_ref(); if !from.exists() { if let Some(msg) = from.to_str() { let msg = format!("Path \"{}\" does not exist or you don't have access!", msg); err!(&msg, ErrorKind::NotFound); } err!( "Path does not exist or you don't have access!", ErrorKind::NotFound ); } let mut to: PathBuf = to.as_ref().to_path_buf(); if !from.is_dir() { if let Some(msg) = from.to_str() { let msg = format!("Path \"{}\" is not a directory!", msg); err!(&msg, ErrorKind::InvalidFolder); } err!("Path is not a directory!", ErrorKind::InvalidFolder); } let dir_name; if let Some(val) = from.components().last() { dir_name = val.as_os_str(); } else { err!("Invalid folder from", ErrorKind::InvalidFolder); } if !(options.content_only || options.copy_inside && !to.exists()) { to.push(dir_name); } let dir_content = get_dir_content(from)?; for directory in dir_content.directories { let tmp_to = Path::new(&directory).strip_prefix(from)?; let dir = to.join(&tmp_to); if !dir.exists() { if options.copy_inside { create_all(dir, false)?; } else { create(dir, false)?; } } } let mut result: u64 = 0; let mut info_process = TransitProcess { copied_bytes: 0, total_bytes: dir_content.dir_size, file_bytes_copied: 0, file_total_bytes: 0, file_name: String::new(), state: TransitState::Normal, }; let mut options = options.clone(); for file in dir_content.files { let mut to = to.to_path_buf(); let tp = Path::new(&file).strip_prefix(from)?; let path = to.join(&tp); let file_name = path.file_name(); if file_name.is_none() { err!("No file name"); } let file_name = file_name.unwrap(); to.push(file_name); let mut file_options = super::file::CopyOptions { overwrite: options.overwrite, skip_exist: options.skip_exist, buffer_size: options.buffer_size, }; if let Some(file_name) = file_name.to_str() { info_process.file_name = file_name.to_string(); } else { err!("Invalid file name", ErrorKind::InvalidFileName); } info_process.file_bytes_copied = 0; info_process.file_total_bytes = Path::new(&file).metadata()?.len(); let mut result_copy: Result; let mut work = true; let copied_bytes = result; while work { { let _progress_handler = |info: super::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 = super::file::move_file_with_progress( &file, &path, &file_options, _progress_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 = TransitState::Exists; let user_decide = progress_handler(info_process); match user_decide { TransitProcessResult::Overwrite => { file_options.overwrite = true; } TransitProcessResult::OverwriteAll => { file_options.overwrite = true; options.overwrite = true; } TransitProcessResult::Skip => { is_remove = false; file_options.skip_exist = true; } TransitProcessResult::SkipAll => { is_remove = false; file_options.skip_exist = true; options.skip_exist = true; } TransitProcessResult::Retry => {} TransitProcessResult::ContinueOrAbort => { let err_msg = err.to_string(); err!(err_msg.as_str(), err.kind) } 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 = TransitState::Exists; let user_decide = progress_handler(info_process); match user_decide { TransitProcessResult::Overwrite => { err!("Overwrite denied for this situation!", ErrorKind::Other); } TransitProcessResult::OverwriteAll => { err!("Overwrite denied for this situation!", ErrorKind::Other); } TransitProcessResult::Skip => { is_remove = false; file_options.skip_exist = true; } TransitProcessResult::SkipAll => { file_options.skip_exist = true; options.skip_exist = true; } TransitProcessResult::Retry => {} TransitProcessResult::ContinueOrAbort => { let err_msg = err.to_string(); err!(err_msg.as_str(), err.kind) } 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) } }, } } } if is_remove { remove(from)?; } Ok(result) } /// Removes directory. /// /// # Example /// ```rust,ignore /// extern crate fs_extra; /// use fs_extra::dir::remove; /// /// remove("source/dir1"); // remove dir1 /// ``` pub fn remove>(path: P) -> Result<()> { if path.as_ref().exists() { Ok(remove_dir_all(path)?) } else { Ok(()) } }