diff options
Diffstat (limited to 'vendor/fs_extra/src/dir.rs')
-rw-r--r-- | vendor/fs_extra/src/dir.rs | 1398 |
1 files changed, 1398 insertions, 0 deletions
diff --git a/vendor/fs_extra/src/dir.rs b/vendor/fs_extra/src/dir.rs new file mode 100644 index 000000000..a5d111249 --- /dev/null +++ b/vendor/fs_extra/src/dir.rs @@ -0,0 +1,1398 @@ +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<String>, + /// List all folders and sub folders directory. + pub directories: Vec<String>, +} + +/// 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<DirEntryAttr, DirEntryValue>, + /// Collection directory entry with information. + pub items: Vec<HashMap<DirEntryAttr, DirEntryValue>>, +} + +/// 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<P>( + path: P, + config: &HashSet<DirEntryAttr>, +) -> Result<HashMap<DirEntryAttr, DirEntryValue>> +where + P: AsRef<Path>, +{ + let path = path.as_ref(); + let metadata = path.metadata()?; + get_details_entry_with_meta(path, config, metadata) +} + +fn get_details_entry_with_meta<P>( + path: P, + config: &HashSet<DirEntryAttr>, + metadata: Metadata, +) -> Result<HashMap<DirEntryAttr, DirEntryValue>> +where + P: AsRef<Path>, +{ + 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<P>(path: P, config: &HashSet<DirEntryAttr>) -> Result<LsResult> +where + P: AsRef<Path>, +{ + 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<P>(path: P, erase: bool) -> Result<()> +where + P: AsRef<Path>, +{ + 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<P>(path: P, erase: bool) -> Result<()> +where + P: AsRef<Path>, +{ + 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<P, Q>(from: P, to: Q, options: &CopyOptions) -> Result<u64> +where + P: AsRef<Path>, + Q: AsRef<Path>, +{ + 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<u64>; + 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<P>(path: P) -> Result<DirContent> +where + P: AsRef<Path>, +{ + 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<P>(path: P, options: &DirOptions) -> Result<DirContent> +where + P: AsRef<Path>, +{ + let mut depth = 0; + if options.depth != 0 { + depth = options.depth + 1; + } + _get_dir_content(path, depth) +} + +fn _get_dir_content<P>(path: P, mut depth: u64) -> Result<DirContent> +where + P: AsRef<Path>, +{ + 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<P>(path: P) -> Result<u64> +where + P: AsRef<Path>, +{ + // 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<P, Q, F>( + from: P, + to: Q, + options: &CopyOptions, + mut progress_handler: F, +) -> Result<u64> +where + P: AsRef<Path>, + Q: AsRef<Path>, + 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<u64>; + 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<P, Q>(from: P, to: Q, options: &CopyOptions) -> Result<u64> +where + P: AsRef<Path>, + Q: AsRef<Path>, +{ + 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<u64>; + 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<P, Q, F>( + from: P, + to: Q, + options: &CopyOptions, + mut progress_handler: F, +) -> Result<u64> +where + P: AsRef<Path>, + Q: AsRef<Path>, + 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<u64>; + 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<P: AsRef<Path>>(path: P) -> Result<()> { + if path.as_ref().exists() { + Ok(remove_dir_all(path)?) + } else { + Ok(()) + } +} |