// Copyright 2019-2021 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT use std::{ fs::{create_dir, create_dir_all, read_dir, remove_dir_all}, path::{Path, PathBuf}, }; /// Directory options. #[derive(Default, Clone)] pub struct DirOpts { pub depth: u64, } /// File options. pub struct FileOpts { pub overwrite: bool, pub skip: bool, pub buffer_size: usize, } /// Copy options. #[derive(Clone)] pub struct Options { pub overwrite: bool, pub skip: bool, pub buffer_size: usize, pub copy_files: bool, pub content_only: bool, pub depth: u64, } /// Directory information descriptor pub struct DirInfo { pub size: u64, pub files: Vec, pub directories: Vec, } impl Default for Options { fn default() -> Options { Options { overwrite: false, skip: false, buffer_size: 64000, copy_files: false, content_only: false, depth: 0, } } } impl Default for FileOpts { fn default() -> FileOpts { FileOpts { overwrite: false, skip: false, buffer_size: 64000, } } } /// Creates the given directory path, /// erasing it first if specified. pub fn create

(path: P, erase: bool) -> crate::Result<()> where P: AsRef, { if erase && path.as_ref().exists() { remove(&path)?; } Ok(create_dir(&path)?) } /// Creates all of the directories of the specified path, /// erasing it first if specified. pub fn create_all

(path: P, erase: bool) -> crate::Result<()> where P: AsRef, { if erase && path.as_ref().exists() { remove(&path)?; } Ok(create_dir_all(&path)?) } /// Removes the directory if it exists. pub fn remove>(path: P) -> crate::Result<()> { if path.as_ref().exists() { Ok(remove_dir_all(path)?) } else { Ok(()) } } /// Copy file with the given options. pub fn copy_file(from: P, to: Q, options: &FileOpts) -> crate::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); return Err(crate::Error::PathUtilError(msg)); } return Err(crate::Error::PathUtilError( "Path does not exist or you don't have access!".to_owned(), )); } if !from.is_file() { if let Some(msg) = from.to_str() { let msg = format!("Path \"{}\" is not a file!", msg); return Err(crate::Error::PathUtilError(msg)); } return Err(crate::Error::PathUtilError( "Path is not a file!".to_owned(), )); } if !options.overwrite && to.as_ref().exists() { if options.skip { return Ok(0); } if let Some(msg) = to.as_ref().to_str() { let msg = format!("Path \"{}\" is exist", msg); return Err(crate::Error::PathUtilError(msg)); } } Ok(std::fs::copy(from, to)?) } /// Copies the directory with the given options. #[allow(dead_code)] pub fn copy(from: P, to: Q, options: &Options) -> crate::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); return Err(crate::Error::PathUtilError(msg)); } return Err(crate::Error::PathUtilError( "Path does not exist or you don't have access".to_owned(), )); } if !from.is_dir() { if let Some(msg) = from.to_str() { let msg = format!("Path \"{}\" is not a directory!", msg); return Err(crate::Error::PathUtilError(msg)); } return Err(crate::Error::PathUtilError( "Path is not a directory".to_owned(), )); } let dir_name = if let Some(val) = from.components().last() { val.as_os_str() } else { return Err(crate::Error::PathUtilError( "Invalid Folder form".to_owned(), )); }; let mut to: PathBuf = to.as_ref().to_path_buf(); if !options.content_only && (!options.copy_files || to.exists()) { to.push(dir_name); } let mut read_options = DirOpts::default(); if options.depth > 0 { read_options.depth = options.depth; } let dir_content = get_dir_info(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_files { 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 = FileOpts { overwrite: options.overwrite, skip: options.skip, buffer_size: options.buffer_size, }; let mut result_copy: crate::Result; let mut work = true; while work { result_copy = copy_file(&file, &path, &file_options); match result_copy { Ok(val) => { result += val; work = false; } Err(err) => { let err_msg = err.to_string(); return Err(crate::Error::PathUtilError(err_msg)); } } } } Ok(result) } /// Gets the DirInfo from the directory path with the given options. pub fn get_dir_info

(path: P, options: &DirOpts) -> crate::Result where P: AsRef, { let depth = if options.depth == 0 { 0 } else { options.depth + 1 }; _get_dir_info(path, depth) } /// Gets the DirInfo from the directory with the given depth. fn _get_dir_info

(path: P, mut depth: u64) -> crate::Result where P: AsRef, { let mut directories = Vec::new(); let mut files = Vec::new(); let mut size = 0; let item = path.as_ref().to_str(); if item.is_none() { return Err(crate::Error::PathUtilError("Invalid Path".to_owned())); } let item = item.expect("Item had no data").to_string(); if path.as_ref().is_dir() { 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_info(_path, depth) { Ok(items) => { let mut _files = items.files; let mut _directories = items.directories; size += items.size; files.append(&mut _files); directories.append(&mut _directories); } Err(err) => return Err(err), } } } } else { size = path.as_ref().metadata()?.len(); files.push(item); } Ok(DirInfo { size, files, directories, }) }