Replace String with PathBuf for paths (#59)

This commit is contained in:
Dariusz Niedoba 2020-10-14 18:41:37 +02:00 committed by GitHub
parent 46af0c0ea2
commit acfecd7ca3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 297 additions and 435 deletions

View File

@ -7,14 +7,16 @@ use crate::common_traits::{DebugPrint, PrintResults, SaveResults};
use crossbeam_channel::Receiver;
use humansize::{file_size_opts as options, FileSize};
use std::collections::BTreeMap;
use std::ffi::OsStr;
use std::fs;
use std::fs::{File, Metadata};
use std::io::Write;
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Clone)]
pub struct FileEntry {
pub path: String,
pub path: PathBuf,
pub size: u64,
pub modified_date: u64,
}
@ -99,26 +101,23 @@ impl BigFile {
fn look_for_big_files(&mut self, rx: Option<&Receiver<()>>) -> bool {
let start_time: SystemTime = SystemTime::now();
let mut folders_to_check: Vec<String> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
// Add root folders for finding
for id in &self.directories.included_directories {
folders_to_check.push(id.to_string());
folders_to_check.push(id.clone());
}
self.information.number_of_checked_folders += folders_to_check.len();
let mut current_folder: String;
let mut next_folder: String;
while !folders_to_check.is_empty() {
if rx.is_some() && rx.unwrap().try_recv().is_ok() {
return false;
}
current_folder = folders_to_check.pop().unwrap();
let current_folder = folders_to_check.pop().unwrap();
let read_dir = match fs::read_dir(&current_folder) {
Ok(t) => t,
Err(_) => {
self.text_messages.warnings.push("Cannot open dir ".to_string() + current_folder.as_str());
self.text_messages.warnings.push(format!("Cannot open dir {}", current_folder.display()));
continue;
} // Permissions denied
};
@ -126,14 +125,14 @@ impl BigFile {
let entry_data = match entry {
Ok(t) => t,
Err(_) => {
self.text_messages.warnings.push("Cannot read entry in dir ".to_string() + current_folder.as_str());
self.text_messages.warnings.push(format!("Cannot read entry in dir {}", current_folder.display()));
continue;
} //Permissions denied
};
let metadata: Metadata = match entry_data.metadata() {
Ok(t) => t,
Err(_) => {
self.text_messages.warnings.push("Cannot read metadata in dir ".to_string() + current_folder.as_str());
self.text_messages.warnings.push(format!("Cannot read metadata in dir {}", current_folder.display()));
continue;
} //Permissions denied
};
@ -144,60 +143,30 @@ impl BigFile {
continue;
}
next_folder = "".to_owned()
+ &current_folder
+ match &entry_data.file_name().into_string() {
Ok(t) => t,
Err(_) => continue,
}
+ "/";
let next_folder = current_folder.join(entry_data.file_name());
if self.directories.is_excluded(&next_folder) || self.excluded_items.is_excluded(&next_folder) {
continue 'dir;
}
for ed in &self.directories.excluded_directories {
if next_folder == *ed {
continue 'dir;
}
}
for expression in &self.excluded_items.items {
if Common::regex_check(expression, &next_folder) {
continue 'dir;
}
}
folders_to_check.push(next_folder);
} else if metadata.is_file() {
let file_name_lowercase: String = match entry_data.file_name().into_string() {
Ok(t) => t,
Err(_) => continue,
}
.to_lowercase();
// Extracting file extension
let file_extension = entry_data.path().extension().and_then(OsStr::to_str).map(str::to_lowercase);
// Checking allowed extensions
if !self.allowed_extensions.file_extensions.is_empty() {
let allowed = self.allowed_extensions.file_extensions.iter().any(|e| file_name_lowercase.ends_with((".".to_string() + e.to_lowercase().as_str()).as_str()));
let allowed = self.allowed_extensions.file_extensions.iter().map(|e| e.to_lowercase()).any(|e| file_extension == Some(e));
if !allowed {
// Not an allowed extension, ignore it.
self.information.number_of_ignored_files += 1;
continue 'dir;
}
}
// Checking files
#[allow(unused_mut)] // Used is later by Windows build
let mut current_file_name = "".to_owned()
+ &current_folder
+ match &entry_data.file_name().into_string() {
Ok(t) => t,
Err(_) => continue,
};
// Checking expressions
for expression in &self.excluded_items.items {
if Common::regex_check(expression, &current_file_name) {
continue 'dir;
}
}
#[cfg(target_family = "windows")]
{
current_file_name = Common::prettier_windows_path(&current_file_name);
let current_file_name = current_folder.join(entry_data.file_name());
if self.excluded_items.is_excluded(&current_file_name) {
continue 'dir;
}
// Creating new file entry
@ -208,12 +177,12 @@ impl BigFile {
Ok(t) => match t.duration_since(UNIX_EPOCH) {
Ok(d) => d.as_secs(),
Err(_) => {
self.text_messages.warnings.push(format!("File {} seems to be modified before Unix Epoch.", current_file_name));
self.text_messages.warnings.push(format!("File {} seems to be modified before Unix Epoch.", current_file_name.display()));
0
}
},
Err(_) => {
self.text_messages.warnings.push("Unable to get modification date from file ".to_string() + current_file_name.as_str());
self.text_messages.warnings.push(format!("Unable to get modification date from file {}", current_file_name.display()));
continue;
} // Permissions Denied
},
@ -318,6 +287,7 @@ impl DebugPrint for BigFile {
println!("-----------------------------------------");
}
}
impl SaveResults for BigFile {
/// Saving results to provided file
fn save_results_to_file(&mut self, file_name: &str) -> bool {
@ -335,35 +305,33 @@ impl SaveResults for BigFile {
}
};
match file.write_all(
format!(
"Results of searching {:?} with excluded directories {:?} and excluded items {:?}\n",
self.directories.included_directories, self.directories.excluded_directories, self.excluded_items.items
)
.as_bytes(),
) {
Ok(_) => (),
Err(_) => {
self.text_messages.errors.push("Failed to save results to file ".to_string() + file_name.as_str());
return false;
}
if writeln!(
file,
"Results of searching {:?} with excluded directories {:?} and excluded items {:?}",
self.directories.included_directories, self.directories.excluded_directories, self.excluded_items.items
)
.is_err()
{
self.text_messages.errors.push(format!("Failed to save results to file {}", file_name));
return false;
}
if self.information.number_of_real_files != 0 {
file.write_all(format!("{} the biggest files.\n\n", self.information.number_of_real_files).as_bytes()).unwrap();
write!(file, "{} the biggest files.\n\n", self.information.number_of_real_files).unwrap();
for (size, files) in self.big_files.iter().rev() {
for file_entry in files {
file.write_all(format!("{} ({}) - {}\n", size.file_size(options::BINARY).unwrap(), size, file_entry.path.clone()).as_bytes()).unwrap();
writeln!(file, "{} ({}) - {}", size.file_size(options::BINARY).unwrap(), size, file_entry.path.display()).unwrap();
}
}
} else {
file.write_all(b"Not found any empty folders.").unwrap();
write!(file, "Not found any empty folders.").unwrap();
}
Common::print_time(start_time, SystemTime::now(), "save_results_to_file".to_string());
true
}
}
impl PrintResults for BigFile {
fn print_results(&self) {
let start_time: SystemTime = SystemTime::now();
@ -371,7 +339,7 @@ impl PrintResults for BigFile {
for (size, vector) in self.big_files.iter().rev() {
// TODO Align all to same width
for entry in vector {
println!("{} ({} bytes) - {}", size.file_size(options::BINARY).unwrap(), size, entry.path);
println!("{} ({} bytes) - {}", size.file_size(options::BINARY).unwrap(), size, entry.path.display());
}
}
Common::print_time(start_time, SystemTime::now(), "print_entries".to_string());

View File

@ -1,5 +1,6 @@
use std::ffi::OsString;
use std::fs;
use std::path::Path;
use std::path::{Path, PathBuf};
use std::time::SystemTime;
/// Class for common functions used across other class/functions
@ -50,7 +51,7 @@ impl Common {
}
/// Function to check if directory match expression
pub fn regex_check(expression: &str, directory: &str) -> bool {
pub fn regex_check(expression: &str, directory: impl AsRef<Path>) -> bool {
if !expression.contains('*') {
#[cfg(debug_assertions)]
{
@ -70,6 +71,9 @@ impl Common {
return false;
}
// Get rid of non unicode characters
let directory = directory.as_ref().to_string_lossy();
// Early checking if directory contains all parts needed by expression
for split in &splits {
if !directory.contains(split) {
@ -102,15 +106,30 @@ impl Common {
}
true
}
#[allow(clippy::ptr_arg)]
pub fn prettier_windows_path(path_to_change: &String) -> String {
path_to_change[..1].to_uppercase() + path_to_change[1..].to_lowercase().replace("\\", "/").as_str()
pub fn normalize_windows_path(path_to_change: impl AsRef<Path>) -> PathBuf {
let path = path_to_change.as_ref();
match path.to_str() {
Some(path) if path.is_char_boundary(1) => {
let replaced = path.replace('\\', "/");
let mut new_path = OsString::new();
if replaced[1..].starts_with(':') {
new_path.push(replaced[..1].to_ascii_uppercase());
new_path.push(replaced[1..].to_ascii_lowercase());
} else {
new_path.push(replaced.to_ascii_lowercase());
}
PathBuf::from(new_path)
}
_ => path.to_path_buf(),
}
}
}
#[cfg(test)]
mod test {
use crate::common::Common;
use std::path::PathBuf;
#[test]
fn test_regex() {
@ -134,8 +153,8 @@ mod test {
}
#[test]
fn test_windows_path() {
assert_eq!("C:/path.txt", Common::prettier_windows_path(&"c:/PATH.tXt".to_string()));
assert_eq!("H:/reka/weza/roman.txt", Common::prettier_windows_path(&"h:/RekA/Weza\\roMan.Txt".to_string()));
assert_eq!("T:/a", Common::prettier_windows_path(&"T:\\A".to_string()));
assert_eq!(PathBuf::from("C:/path.txt"), Common::normalize_windows_path("c:/PATH.tXt"));
assert_eq!(PathBuf::from("H:/reka/weza/roman.txt"), Common::normalize_windows_path("h:/RekA/Weza\\roMan.Txt"));
assert_eq!(PathBuf::from("T:/a"), Common::normalize_windows_path("T:\\A"));
}
}

View File

@ -1,12 +1,12 @@
use crate::common::Common;
use crate::common_messages::Messages;
use std::path::Path;
use std::path::{Path, PathBuf};
use std::time::SystemTime;
#[derive(Default)]
pub struct Directories {
pub excluded_directories: Vec<String>,
pub included_directories: Vec<String>,
pub excluded_directories: Vec<PathBuf>,
pub included_directories: Vec<PathBuf>,
}
impl Directories {
pub fn new() -> Self {
@ -14,7 +14,7 @@ impl Directories {
}
/// Setting included directories, at least one must be provided
pub fn set_included_directory(&mut self, mut included_directory: String, text_messages: &mut Messages) -> bool {
pub fn set_included_directory(&mut self, included_directory: String, text_messages: &mut Messages) -> bool {
let start_time: SystemTime = SystemTime::now();
if included_directory.is_empty() {
@ -22,45 +22,28 @@ impl Directories {
return false;
}
included_directory = included_directory.replace("\"", "");
let directories: Vec<String> = included_directory.split(',').map(String::from).collect();
let mut checked_directories: Vec<String> = Vec::new();
let included_directory = included_directory.replace("\"", "");
let directories: Vec<_> = included_directory.split(',').map(|dir| dir.trim()).filter(|dir| !dir.is_empty()).map(PathBuf::from).collect();
let mut checked_directories: Vec<PathBuf> = Vec::new();
for directory in directories {
let directory: String = directory.trim().to_string();
if directory == "" {
if directory.to_string_lossy().contains('*') {
text_messages.warnings.push(format!("Included Directory Warning: Wildcards in path are not supported, ignoring {}", directory.display()));
continue;
}
if directory.contains('*') {
text_messages.warnings.push("Included Directory Warning: Wildcards in path are not supported, ignoring ".to_string() + directory.as_str());
if directory.is_relative() {
text_messages.warnings.push(format!("Included Directory Warning: Relative path are not supported, ignoring {}", directory.display()));
continue;
}
#[cfg(target_family = "unix")]
if !directory.starts_with('/') {
text_messages.warnings.push("Included Directory Warning: Relative path are not supported, ignoring ".to_string() + directory.as_str());
if !directory.exists() {
text_messages.warnings.push(format!("Included Directory Warning: Provided folder path must exits, ignoring {}", directory.display()));
continue;
}
#[cfg(target_family = "windows")]
if !(directory[..directory.len()].starts_with(":/") || !directory[..directory.len()].starts_with(":\\")) {
text_messages.warnings.push("Included Directory Warning: Relative path are not supported, ignoring ".to_string() + directory.as_str());
if !directory.is_dir() {
text_messages.warnings.push(format!("Included Directory Warning: Provided path must point at the directory, ignoring {}", directory.display()));
continue;
}
if !Path::new(&directory).exists() {
text_messages.warnings.push("Included Directory Warning: Provided folder path must exits, ignoring ".to_string() + directory.as_str());
continue;
}
if !Path::new(&directory).is_dir() {
text_messages.warnings.push("Included Directory Warning: Provided path must point at the directory, ignoring ".to_string() + directory.as_str());
continue;
}
// directory must end with /, due to possibility of incorrect assumption, that e.g. /home/rafal is top folder to /home/rafalinho
if !directory.ends_with('/') {
checked_directories.push(directory + "/");
} else {
checked_directories.push(directory);
}
checked_directories.push(directory);
}
if checked_directories.is_empty() {
@ -75,51 +58,40 @@ impl Directories {
}
/// Setting absolute path to exclude
pub fn set_excluded_directory(&mut self, mut excluded_directory: String, text_messages: &mut Messages) {
pub fn set_excluded_directory(&mut self, excluded_directory: String, text_messages: &mut Messages) {
let start_time: SystemTime = SystemTime::now();
if excluded_directory.is_empty() {
return;
}
excluded_directory = excluded_directory.replace("\"", "");
let directories: Vec<String> = excluded_directory.split(',').map(String::from).collect();
let mut checked_directories: Vec<String> = Vec::new();
let excluded_directory = excluded_directory.replace("\"", "");
let directories: Vec<PathBuf> = excluded_directory.split(',').map(|dir| dir.trim()).filter(|dir| !dir.is_empty()).map(PathBuf::from).collect();
let mut checked_directories: Vec<PathBuf> = Vec::new();
for directory in directories {
let directory: String = directory.trim().to_string().replace("\\", "/");
if directory == "" {
continue;
}
if directory == "/" {
let directory_as_string = directory.to_string_lossy();
if directory_as_string == "/" {
text_messages.errors.push("Excluded Directory ERROR: Excluding / is pointless, because it means that no files will be scanned.".to_string());
break;
}
if directory.contains('*') {
text_messages.warnings.push("Excluded Directory Warning: Wildcards in path are not supported, ignoring ".to_string() + directory.as_str());
if directory_as_string.contains('*') {
text_messages.warnings.push(format!("Excluded Directory Warning: Wildcards in path are not supported, ignoring {}", directory.display()));
continue;
}
#[cfg(target_family = "unix")]
if !directory.starts_with('/') {
text_messages.warnings.push("Excluded Directory Warning: Relative path are not supported, ignoring ".to_string() + directory.as_str());
if directory.is_relative() {
text_messages.warnings.push(format!("Excluded Directory Warning: Relative path are not supported, ignoring {}", directory.display()));
continue;
}
#[cfg(target_family = "windows")]
if !(directory[..directory.len()].starts_with(":/") || !directory[..directory.len()].starts_with(":\\")) {
text_messages.warnings.push("Excluded Directory Warning: Relative path are not supported, ignoring ".to_string() + directory.as_str());
if !directory.exists() {
text_messages.warnings.push(format!("Excluded Directory Warning: Provided folder path must exits, ignoring {}", directory.display()));
continue;
}
if !Path::new(&directory).is_dir() {
text_messages.warnings.push("Excluded Directory Warning: Provided path must point at the directory, ignoring ".to_string() + directory.as_str());
if !directory.is_dir() {
text_messages.warnings.push(format!("Excluded Directory Warning: Provided path must point at the directory, ignoring {}", directory.display()));
continue;
}
// directory must end with /, due to possibility of incorrect assumption, that e.g. /home/rafal is top folder to /home/rafalinho
if !directory.ends_with('/') {
checked_directories.push(directory.trim().to_string() + "/");
} else {
checked_directories.push(directory.trim().to_string());
}
checked_directories.push(directory);
}
self.excluded_directories = checked_directories;
@ -130,14 +102,12 @@ impl Directories {
pub fn optimize_directories(&mut self, recursive_search: bool, text_messages: &mut Messages) -> bool {
let start_time: SystemTime = SystemTime::now();
let mut optimized_included: Vec<String> = Vec::<String>::new();
let mut optimized_excluded: Vec<String> = Vec::<String>::new();
let mut optimized_included: Vec<PathBuf> = Vec::new();
let mut optimized_excluded: Vec<PathBuf> = Vec::new();
// Windows(or specific EXT4 extension) doesn't recognize size of letters so we must remove one of directory e.g. - C:/h.txt, C:/H.txt
#[cfg(target_family = "windows")]
{
self.included_directories = self.included_directories.iter().map(Common::prettier_windows_path).collect();
self.excluded_directories = self.excluded_directories.iter().map(Common::prettier_windows_path).collect();
if cfg!(target_family = "windows") {
self.included_directories = self.included_directories.iter().map(Common::normalize_windows_path).collect();
self.excluded_directories = self.excluded_directories.iter().map(Common::normalize_windows_path).collect();
}
// Remove duplicated entries like: "/", "/"
@ -165,7 +135,7 @@ impl Directories {
}
}
if !is_inside {
optimized_excluded.push(ed_checked.to_string());
optimized_excluded.push(ed_checked.clone());
}
}
@ -182,14 +152,14 @@ impl Directories {
}
}
if !is_inside {
optimized_included.push(id_checked.to_string());
optimized_included.push(id_checked.clone());
}
}
self.included_directories = optimized_included;
optimized_included = Vec::<String>::new();
optimized_included = Vec::new();
self.excluded_directories = optimized_excluded;
optimized_excluded = Vec::<String>::new();
optimized_excluded = Vec::new();
}
// Remove included directories which are inside any excluded directory
@ -202,31 +172,30 @@ impl Directories {
}
}
if !is_inside {
optimized_included.push(id.to_string());
optimized_included.push(id.clone());
}
}
self.included_directories = optimized_included;
optimized_included = Vec::<String>::new();
optimized_included = Vec::new();
// Remove non existed directories
for id in &self.included_directories {
let path = Path::new(id);
if path.exists() {
optimized_included.push(id.to_string());
optimized_included.push(id.clone());
}
}
for ed in &self.excluded_directories {
let path = Path::new(ed);
if path.exists() {
optimized_excluded.push(ed.to_string());
optimized_excluded.push(ed.clone());
}
}
self.included_directories = optimized_included;
// optimized_included = Vec::<String>::new();
self.excluded_directories = optimized_excluded;
optimized_excluded = Vec::<String>::new();
optimized_excluded = Vec::new();
// Excluded paths must are inside included path, because
for ed in &self.excluded_directories {
@ -238,12 +207,11 @@ impl Directories {
}
}
if is_inside {
optimized_excluded.push(ed.to_string());
optimized_excluded.push(ed.clone());
}
}
self.excluded_directories = optimized_excluded;
// optimized_excluded = Vec::<String>::new();
if self.included_directories.is_empty() {
text_messages.errors.push("Optimize Directories ERROR: Excluded directories overlaps all included directories.".to_string());
@ -256,4 +224,13 @@ impl Directories {
Common::print_time(start_time, SystemTime::now(), "optimize_directories".to_string());
true
}
/// Checks whether a specified directory is excluded from searching
pub fn is_excluded(&self, path: impl AsRef<Path>) -> bool {
let path = path.as_ref();
#[cfg(target_family = "windows")]
let path = Common::normalize_windows_path(path);
// We're assuming that `excluded_directories` are already normalized
self.excluded_directories.iter().any(|p| p.as_path() == path)
}
}

View File

@ -1,5 +1,6 @@
use crate::common::Common;
use crate::common_messages::Messages;
use std::path::Path;
use std::time::SystemTime;
#[derive(Default)]
@ -45,4 +46,17 @@ impl ExcludedItems {
self.items = checked_expressions;
Common::print_time(start_time, SystemTime::now(), "set_excluded_items".to_string());
}
/// Checks whether a specified path is excluded from searching
pub fn is_excluded(&self, path: impl AsRef<Path>) -> bool {
#[cfg(target_family = "windows")]
let path = Common::normalize_windows_path(path);
for expression in &self.items {
if Common::regex_check(expression, &path) {
return true;
}
}
false
}
}

View File

@ -4,6 +4,7 @@ use std::collections::{BTreeMap, HashMap};
use std::fs;
use std::fs::{File, Metadata};
use std::io::prelude::*;
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};
use crate::common::Common;
@ -34,7 +35,7 @@ pub enum DeleteMethod {
#[derive(Clone)]
pub struct FileEntry {
pub path: String,
pub path: PathBuf,
pub size: u64,
pub modified_date: u64,
}
@ -178,27 +179,25 @@ impl DuplicateFinder {
fn check_files_size(&mut self, rx: Option<&Receiver<()>>) -> bool {
// TODO maybe add multithreading checking for file hash
let start_time: SystemTime = SystemTime::now();
let mut folders_to_check: Vec<String> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
// Add root folders for finding
for id in &self.directories.included_directories {
folders_to_check.push(id.to_string());
folders_to_check.push(id.clone());
}
self.information.number_of_checked_folders += folders_to_check.len();
let mut current_folder: String;
let mut next_folder: String;
while !folders_to_check.is_empty() {
if rx.is_some() && rx.unwrap().try_recv().is_ok() {
return false;
}
current_folder = folders_to_check.pop().unwrap();
let current_folder = folders_to_check.pop().unwrap();
// Read current dir, if permission are denied just go to next
let read_dir = match fs::read_dir(&current_folder) {
Ok(t) => t,
Err(_) => {
self.text_messages.warnings.push("Cannot open dir ".to_string() + current_folder.as_str());
self.text_messages.warnings.push(format!("Cannot open dir {}", current_folder.display()));
continue;
} // Permissions denied
};
@ -208,14 +207,14 @@ impl DuplicateFinder {
let entry_data = match entry {
Ok(t) => t,
Err(_) => {
self.text_messages.warnings.push("Cannot read entry in dir ".to_string() + current_folder.as_str());
self.text_messages.warnings.push(format!("Cannot read entry in dir {}", current_folder.display()));
continue;
} //Permissions denied
};
let metadata: Metadata = match entry_data.metadata() {
Ok(t) => t,
Err(_) => {
self.text_messages.warnings.push("Cannot read metadata in dir ".to_string() + current_folder.as_str());
self.text_messages.warnings.push(format!("Cannot read metadata in dir {}", current_folder.display()));
continue;
} //Permissions denied
};
@ -226,24 +225,15 @@ impl DuplicateFinder {
continue;
}
next_folder = "".to_owned()
+ &current_folder
+ match &entry_data.file_name().into_string() {
Ok(t) => t,
Err(_) => continue,
}
+ "/";
let next_folder = current_folder.join(entry_data.file_name());
if self.directories.is_excluded(&next_folder) {
continue 'dir;
}
for ed in &self.directories.excluded_directories {
if next_folder == *ed {
continue 'dir;
}
}
for expression in &self.excluded_items.items {
if Common::regex_check(expression, &next_folder) {
continue 'dir;
}
if self.excluded_items.is_excluded(&next_folder) {
continue 'dir;
}
folders_to_check.push(next_folder);
} else if metadata.is_file() {
// let mut have_valid_extension: bool;
@ -264,24 +254,9 @@ impl DuplicateFinder {
}
// Checking files
if metadata.len() >= self.minimal_file_size {
#[allow(unused_mut)] // Used is later by Windows build
let mut current_file_name = "".to_owned()
+ &current_folder
+ match &entry_data.file_name().into_string() {
Ok(t) => t,
Err(_) => continue,
};
// Checking expressions
for expression in &self.excluded_items.items {
if Common::regex_check(expression, &current_file_name) {
continue 'dir;
}
}
#[cfg(target_family = "windows")]
{
current_file_name = Common::prettier_windows_path(&current_file_name);
let current_file_name = current_folder.join(entry_data.file_name());
if self.excluded_items.is_excluded(&current_file_name) {
continue 'dir;
}
// Creating new file entry
@ -292,12 +267,12 @@ impl DuplicateFinder {
Ok(t) => match t.duration_since(UNIX_EPOCH) {
Ok(d) => d.as_secs(),
Err(_) => {
self.text_messages.warnings.push(format!("File {} seems to be modified before Unix Epoch.", current_file_name));
self.text_messages.warnings.push(format!("File {} seems to be modified before Unix Epoch.", current_file_name.display()));
0
}
},
Err(_) => {
self.text_messages.warnings.push("Unable to get modification date from file ".to_string() + current_file_name.as_str());
self.text_messages.warnings.push(format!("Unable to get modification date from file {}", current_file_name.display()));
continue;
} // Permissions Denied
},
@ -354,7 +329,7 @@ impl DuplicateFinder {
file_handler = match File::open(&file_entry.path) {
Ok(t) => t,
Err(_) => {
self.text_messages.warnings.push("Unable to check hash of file ".to_string() + file_entry.path.as_str());
self.text_messages.warnings.push(format!("Unable to check hash of file {}", file_entry.path.display()));
continue;
}
};
@ -368,7 +343,7 @@ impl DuplicateFinder {
let n = match file_handler.read(&mut buffer) {
Ok(t) => t,
Err(_) => {
self.text_messages.warnings.push("Error happened when checking hash of file ".to_string() + file_entry.path.as_str());
self.text_messages.warnings.push(format!("Error happened when checking hash of file {}", file_entry.path.display()));
error_reading_file = true;
break;
}
@ -523,62 +498,55 @@ impl SaveResults for DuplicateFinder {
}
};
match file.write_all(
format!(
"Results of searching {:?} with excluded directories {:?} and excluded items {:?}\n",
self.directories.included_directories, self.directories.excluded_directories, self.excluded_items.items
)
.as_bytes(),
) {
Ok(_) => (),
Err(_) => {
self.text_messages.errors.push(format!("Failed to save results to file {}", file_name));
return false;
}
if writeln!(
file,
"Results of searching {:?} with excluded directories {:?} and excluded items {:?}",
self.directories.included_directories, self.directories.excluded_directories, self.excluded_items.items
)
.is_err()
{
self.text_messages.errors.push(format!("Failed to save results to file {}", file_name));
return false;
}
if !self.files_with_identical_size.is_empty() {
file.write_all(b"-------------------------------------------------Files with same size-------------------------------------------------\n").unwrap();
file.write_all(
format!(
"Found {} duplicated files which in {} groups which takes {}.\n",
self.information.number_of_duplicated_files_by_size,
self.information.number_of_groups_by_size,
self.information.lost_space_by_size.file_size(options::BINARY).unwrap()
)
.as_bytes(),
writeln!(file, "-------------------------------------------------Files with same size-------------------------------------------------").unwrap();
writeln!(
file,
"Found {} duplicated files which in {} groups which takes {}.",
self.information.number_of_duplicated_files_by_size,
self.information.number_of_groups_by_size,
self.information.lost_space_by_size.file_size(options::BINARY).unwrap()
)
.unwrap();
for (size, vector) in self.files_with_identical_size.iter().rev() {
file.write_all(format!("\n---- Size {} ({}) - {} files \n", size.file_size(options::BINARY).unwrap(), size, vector.len()).as_bytes()).unwrap();
write!(file, "\n---- Size {} ({}) - {} files \n", size.file_size(options::BINARY).unwrap(), size, vector.len()).unwrap();
for file_entry in vector {
file.write_all(format!("{} \n", file_entry.path).as_bytes()).unwrap();
writeln!(file, "{}", file_entry.path.display()).unwrap();
}
}
if !self.files_with_identical_hashes.is_empty() {
file.write_all(b"-------------------------------------------------Files with same hashes-------------------------------------------------\n").unwrap();
file.write_all(
format!(
"Found {} duplicated files which in {} groups which takes {}.\n",
self.information.number_of_duplicated_files_by_hash,
self.information.number_of_groups_by_hash,
self.information.lost_space_by_hash.file_size(options::BINARY).unwrap()
)
.as_bytes(),
writeln!(file, "-------------------------------------------------Files with same hashes-------------------------------------------------").unwrap();
writeln!(
file,
"Found {} duplicated files which in {} groups which takes {}.",
self.information.number_of_duplicated_files_by_hash,
self.information.number_of_groups_by_hash,
self.information.lost_space_by_hash.file_size(options::BINARY).unwrap()
)
.unwrap();
for (size, vectors_vector) in self.files_with_identical_hashes.iter().rev() {
for vector in vectors_vector {
file.write_all(format!("\n---- Size {} ({}) - {} files \n", size.file_size(options::BINARY).unwrap(), size, vector.len()).as_bytes()).unwrap();
writeln!(file, "\n---- Size {} ({}) - {} files", size.file_size(options::BINARY).unwrap(), size, vector.len()).unwrap();
for file_entry in vector {
file.write_all(format!("{} \n", file_entry.path).as_bytes()).unwrap();
writeln!(file, "{}", file_entry.path.display()).unwrap();
}
}
}
}
} else {
file.write_all(b"Not found any duplicates.").unwrap();
write!(file, "Not found any duplicates.").unwrap();
}
Common::print_time(start_time, SystemTime::now(), "save_results_to_file".to_string());
true
@ -610,7 +578,7 @@ impl PrintResults for DuplicateFinder {
for j in vector {
println!("Size - {} ({}) - {} files ", size.file_size(options::BINARY).unwrap(), size, j.len());
for k in j {
println!("{}", k.path);
println!("{}", k.path.display());
}
println!("----");
}
@ -631,7 +599,7 @@ impl PrintResults for DuplicateFinder {
for (size, vector) in &self.files_with_identical_size {
println!("Size - {} ({}) - {} files ", size.file_size(options::BINARY).unwrap(), size, vector.len());
for j in vector {
println!("{}", j.path);
println!("{}", j.path.display());
}
println!();
}
@ -670,7 +638,7 @@ fn delete_files(vector: &[FileEntry], delete_method: &DeleteMethod, warnings: &m
}
Err(_) => {
failed_to_remove_files += 1;
warnings.push("Failed to delete".to_string() + vector[q_index].path.as_str());
warnings.push(format!("Failed to delete {}", vector[q_index].path.display()));
}
};
}
@ -688,7 +656,7 @@ fn delete_files(vector: &[FileEntry], delete_method: &DeleteMethod, warnings: &m
}
Err(_) => {
failed_to_remove_files += 1;
warnings.push("Failed to delete".to_string() + vector[q_index].path.as_str());
warnings.push(format!("Failed to delete {}", vector[q_index].path.display()));
}
};
}
@ -708,7 +676,7 @@ fn delete_files(vector: &[FileEntry], delete_method: &DeleteMethod, warnings: &m
}
Err(_) => {
failed_to_remove_files += 1;
warnings.push("Failed to delete".to_string() + file.path.as_str());
warnings.push(format!("Failed to delete {}", file.path.display()));
}
};
}
@ -730,7 +698,7 @@ fn delete_files(vector: &[FileEntry], delete_method: &DeleteMethod, warnings: &m
}
Err(_) => {
failed_to_remove_files += 1;
warnings.push("Failed to delete".to_string() + file.path.as_str());
warnings.push(format!("Failed to delete {}", file.path.display()));
}
};
}

View File

@ -1,6 +1,7 @@
use std::fs;
use std::fs::{File, Metadata};
use std::io::prelude::*;
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};
use crate::common::Common;
@ -19,7 +20,7 @@ pub enum DeleteMethod {
#[derive(Clone)]
pub struct FileEntry {
pub path: String,
pub path: PathBuf,
pub modified_date: u64,
}
@ -121,27 +122,25 @@ impl EmptyFiles {
/// Check files for any with size == 0
fn check_files(&mut self, rx: Option<&Receiver<()>>) -> bool {
let start_time: SystemTime = SystemTime::now();
let mut folders_to_check: Vec<String> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
// Add root folders for finding
for id in &self.directories.included_directories {
folders_to_check.push(id.to_string());
folders_to_check.push(id.clone());
}
self.information.number_of_checked_folders += folders_to_check.len();
let mut current_folder: String;
let mut next_folder: String;
while !folders_to_check.is_empty() {
if rx.is_some() && rx.unwrap().try_recv().is_ok() {
return false;
}
current_folder = folders_to_check.pop().unwrap();
let current_folder = folders_to_check.pop().unwrap();
// Read current dir, if permission are denied just go to next
let read_dir = match fs::read_dir(&current_folder) {
Ok(t) => t,
Err(_) => {
self.text_messages.warnings.push("Cannot open dir ".to_string() + current_folder.as_str());
self.text_messages.warnings.push(format!("Cannot open dir {}", current_folder.display()));
continue;
} // Permissions denied
};
@ -151,14 +150,14 @@ impl EmptyFiles {
let entry_data = match entry {
Ok(t) => t,
Err(_) => {
self.text_messages.warnings.push("Cannot read entry in dir ".to_string() + current_folder.as_str());
self.text_messages.warnings.push(format!("Cannot read entry in dir {}", current_folder.display()));
continue;
} //Permissions denied
};
let metadata: Metadata = match entry_data.metadata() {
Ok(t) => t,
Err(_) => {
self.text_messages.warnings.push("Cannot read metadata in dir ".to_string() + current_folder.as_str());
self.text_messages.warnings.push(format!("Cannot read metadata in dir {}", current_folder.display()));
continue;
} //Permissions denied
};
@ -169,24 +168,11 @@ impl EmptyFiles {
continue;
}
next_folder = "".to_owned()
+ &current_folder
+ match &entry_data.file_name().into_string() {
Ok(t) => t,
Err(_) => continue,
}
+ "/";
let next_folder = current_folder.join(entry_data.file_name());
if self.directories.is_excluded(&next_folder) || self.excluded_items.is_excluded(&next_folder) {
continue 'dir;
}
for ed in &self.directories.excluded_directories {
if next_folder == *ed {
continue 'dir;
}
}
for expression in &self.excluded_items.items {
if Common::regex_check(expression, &next_folder) {
continue 'dir;
}
}
folders_to_check.push(next_folder);
} else if metadata.is_file() {
let file_name_lowercase: String = match entry_data.file_name().into_string() {
@ -206,23 +192,9 @@ impl EmptyFiles {
}
// Checking files
if metadata.len() == 0 {
#[allow(unused_mut)] // Used is later by Windows build
let mut current_file_name = "".to_owned()
+ &current_folder
+ match &entry_data.file_name().into_string() {
Ok(t) => t,
Err(_) => continue,
};
// Checking expressions
for expression in &self.excluded_items.items {
if Common::regex_check(expression, &current_file_name) {
continue 'dir;
}
}
#[cfg(target_family = "windows")]
{
current_file_name = Common::prettier_windows_path(&current_file_name);
let current_file_name = current_folder.join(entry_data.file_name());
if self.excluded_items.is_excluded(&current_file_name) {
continue 'dir;
}
// Creating new file entry
@ -232,12 +204,12 @@ impl EmptyFiles {
Ok(t) => match t.duration_since(UNIX_EPOCH) {
Ok(d) => d.as_secs(),
Err(_) => {
self.text_messages.warnings.push(format!("File {} seems to be modified before Unix Epoch.", current_file_name));
self.text_messages.warnings.push(format!("File {} seems to be modified before Unix Epoch.", current_file_name.display()));
0
}
},
Err(_) => {
self.text_messages.warnings.push("Unable to get modification date from file ".to_string() + current_file_name.as_str());
self.text_messages.warnings.push(format!("Unable to get modification date from file {}", current_file_name.display()));
continue;
} // Permissions Denied
},
@ -270,7 +242,7 @@ impl EmptyFiles {
DeleteMethod::Delete => {
for file_entry in &self.empty_files {
if fs::remove_file(file_entry.path.clone()).is_err() {
self.text_messages.warnings.push(file_entry.path.clone());
self.text_messages.warnings.push(file_entry.path.display().to_string());
}
}
}
@ -338,27 +310,24 @@ impl SaveResults for EmptyFiles {
}
};
match file.write_all(
format!(
"Results of searching {:?} with excluded directories {:?} and excluded items {:?}\n",
self.directories.included_directories, self.directories.excluded_directories, self.excluded_items.items
)
.as_bytes(),
) {
Ok(_) => (),
Err(_) => {
self.text_messages.errors.push(format!("Failed to save results to file {}", file_name));
return false;
}
if writeln!(
file,
"Results of searching {:?} with excluded directories {:?} and excluded items {:?}",
self.directories.included_directories, self.directories.excluded_directories, self.excluded_items.items
)
.is_err()
{
self.text_messages.errors.push(format!("Failed to save results to file {}", file_name));
return false;
}
if !self.empty_files.is_empty() {
file.write_all(format!("Found {} empty files.\n", self.information.number_of_empty_files).as_bytes()).unwrap();
writeln!(file, "Found {} empty files.", self.information.number_of_empty_files).unwrap();
for file_entry in self.empty_files.iter() {
file.write_all(format!("{} \n", file_entry.path).as_bytes()).unwrap();
writeln!(file, "{}", file_entry.path.display()).unwrap();
}
} else {
file.write_all(b"Not found any empty files.").unwrap();
write!(file, "Not found any empty files.").unwrap();
}
Common::print_time(start_time, SystemTime::now(), "save_results_to_file".to_string());
true
@ -371,7 +340,7 @@ impl PrintResults for EmptyFiles {
let start_time: SystemTime = SystemTime::now();
println!("Found {} empty files.\n", self.information.number_of_empty_files);
for file_entry in self.empty_files.iter() {
println!("{}", file_entry.path);
println!("{}", file_entry.path.display());
}
Common::print_time(start_time, SystemTime::now(), "print_entries".to_string());

View File

@ -7,6 +7,7 @@ use std::collections::BTreeMap;
use std::fs;
use std::fs::{File, Metadata};
use std::io::Write;
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};
/// Enum with values which show if folder is empty.
@ -20,7 +21,7 @@ enum FolderEmptiness {
/// Struct assigned to each checked folder with parent path(used to ignore parent if children are not empty) and flag which shows if folder is empty
#[derive(Clone)]
pub struct FolderEntry {
parent_path: Option<String>, // Usable only when finding
parent_path: Option<PathBuf>, // Usable only when finding
is_empty: FolderEmptiness,
pub modified_date: u64,
}
@ -30,7 +31,7 @@ pub struct EmptyFolder {
information: Info,
delete_folders: bool,
text_messages: Messages,
empty_folder_list: BTreeMap<String, FolderEntry>, // Path, FolderEntry
empty_folder_list: BTreeMap<PathBuf, FolderEntry>, // Path, FolderEntry
directories: Directories,
stopped_search: bool,
}
@ -65,7 +66,7 @@ impl EmptyFolder {
self.stopped_search
}
pub const fn get_empty_folder_list(&self) -> &BTreeMap<String, FolderEntry> {
pub const fn get_empty_folder_list(&self) -> &BTreeMap<PathBuf, FolderEntry> {
&self.empty_folder_list
}
@ -97,7 +98,7 @@ impl EmptyFolder {
/// Clean directory tree
/// If directory contains only 2 empty folders, then this directory should be removed instead two empty folders inside because it will produce another empty folder.
fn optimize_folders(&mut self) {
let mut new_directory_folders: BTreeMap<String, FolderEntry> = Default::default();
let mut new_directory_folders: BTreeMap<PathBuf, FolderEntry> = Default::default();
for (name, folder_entry) in &self.empty_folder_list {
match &folder_entry.parent_path {
@ -119,8 +120,8 @@ impl EmptyFolder {
/// Parameter initial_checking for second check before deleting to be sure that checked folder is still empty
fn check_for_empty_folders(&mut self, rx: Option<&Receiver<()>>) -> bool {
let start_time: SystemTime = SystemTime::now();
let mut folders_to_check: Vec<String> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
let mut folders_checked: BTreeMap<String, FolderEntry> = Default::default();
let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
let mut folders_checked: BTreeMap<PathBuf, FolderEntry> = Default::default();
// Add root folders for finding
for id in &self.directories.included_directories {
@ -135,14 +136,12 @@ impl EmptyFolder {
folders_to_check.push(id.clone());
}
let mut current_folder: String;
let mut next_folder: String;
while !folders_to_check.is_empty() {
if rx.is_some() && rx.unwrap().try_recv().is_ok() {
return false;
}
self.information.number_of_checked_folders += 1;
current_folder = folders_to_check.pop().unwrap();
let current_folder = folders_to_check.pop().unwrap();
// Checked folder may be deleted or we may not have permissions to open it so we assume that this folder is not be empty
let read_dir = match fs::read_dir(&current_folder) {
Ok(t) => t,
@ -163,15 +162,8 @@ impl EmptyFolder {
};
// If child is dir, still folder may be considered as empty if all children are only directories.
if metadata.is_dir() {
next_folder = "".to_owned()
+ &current_folder
+ match &entry_data.file_name().into_string() {
Ok(t) => t,
Err(_) => continue,
}
+ "/";
let next_folder = current_folder.join(entry_data.file_name());
folders_to_check.push(next_folder.clone());
folders_checked.insert(
next_folder.clone(),
FolderEntry {
@ -181,12 +173,12 @@ impl EmptyFolder {
Ok(t) => match t.duration_since(UNIX_EPOCH) {
Ok(d) => d.as_secs(),
Err(_) => {
self.text_messages.warnings.push(format!("Folder {} seems to be modified before Unix Epoch.", current_folder));
self.text_messages.warnings.push(format!("Folder {} seems to be modified before Unix Epoch.", current_folder.display()));
0
}
},
Err(_) => {
self.text_messages.warnings.push(format!("Failed to read modification date of folder {}", current_folder));
self.text_messages.warnings.push(format!("Failed to read modification date of folder {}", current_folder.display()));
continue;
}
},
@ -196,12 +188,11 @@ impl EmptyFolder {
// Not folder so it may be a file or symbolic link so it isn't empty
folders_checked.get_mut(&current_folder).unwrap().is_empty = FolderEmptiness::No;
let mut d = folders_checked.get_mut(&current_folder).unwrap();
let mut cf: String;
// Loop to recursively set as non empty this and all his parent folders
loop {
d.is_empty = FolderEmptiness::No;
if d.parent_path != None {
cf = d.parent_path.clone().unwrap();
let cf = d.parent_path.clone().unwrap();
d = folders_checked.get_mut(&cf).unwrap();
} else {
break;
@ -215,11 +206,6 @@ impl EmptyFolder {
#[allow(unused_mut)] // Used is later by Windows build
for (mut name, folder_entry) in folders_checked {
if folder_entry.is_empty != FolderEmptiness::No {
#[cfg(target_family = "windows")]
{
name = Common::prettier_windows_path(&name);
}
self.empty_folder_list.insert(name, folder_entry);
}
}
@ -235,7 +221,7 @@ impl EmptyFolder {
for name in self.empty_folder_list.keys() {
match fs::remove_dir_all(name) {
Ok(_) => (),
Err(_) => self.text_messages.warnings.push(format!("Failed to remove folder {}", name)),
Err(_) => self.text_messages.warnings.push(format!("Failed to remove folder {}", name.display())),
};
}
@ -285,22 +271,19 @@ impl SaveResults for EmptyFolder {
}
};
match file.write_all(format!("Results of searching {:?} with excluded directories {:?}\n", self.directories.included_directories, self.directories.excluded_directories).as_bytes()) {
Ok(_) => (),
Err(_) => {
self.text_messages.errors.push("Failed to save results to file ".to_string() + file_name.as_str());
return false;
}
if writeln!(file, "Results of searching {:?} with excluded directories {:?}", self.directories.included_directories, self.directories.excluded_directories).is_err() {
self.text_messages.errors.push("Failed to save results to file ".to_string() + file_name.as_str());
return false;
}
if !self.empty_folder_list.is_empty() {
file.write_all(b"-------------------------------------------------Empty folder list-------------------------------------------------\n").unwrap();
file.write_all(("Found ".to_string() + self.information.number_of_empty_folders.to_string().as_str() + " empty folders\n").as_bytes()).unwrap();
writeln!(file, "-------------------------------------------------Empty folder list-------------------------------------------------").unwrap();
writeln!(file, "Found {} empty folders", self.information.number_of_empty_folders).unwrap();
for name in self.empty_folder_list.keys() {
file.write_all((name.clone() + "\n").as_bytes()).unwrap();
writeln!(file, "{}", name.display()).unwrap();
}
} else {
file.write_all(b"Not found any empty folders.").unwrap();
write!(file, "Not found any empty folders.").unwrap();
}
Common::print_time(start_time, SystemTime::now(), "save_results_to_file".to_string());
true
@ -313,7 +296,7 @@ impl PrintResults for EmptyFolder {
println!("Found {} empty folders", self.empty_folder_list.len());
}
for name in self.empty_folder_list.keys() {
println!("{}", name);
println!("{}", name.display());
}
}
}

View File

@ -1,6 +1,7 @@
use std::fs;
use std::fs::{File, Metadata};
use std::io::prelude::*;
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};
use crate::common::Common;
@ -18,7 +19,7 @@ pub enum DeleteMethod {
#[derive(Clone)]
pub struct FileEntry {
pub path: String,
pub path: PathBuf,
pub modified_date: u64,
}
@ -112,31 +113,25 @@ impl Temporary {
fn check_files(&mut self, rx: Option<&Receiver<()>>) -> bool {
let start_time: SystemTime = SystemTime::now();
let mut folders_to_check: Vec<String> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
let mut folders_to_check: Vec<PathBuf> = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector
// Add root folders for finding
for id in &self.directories.included_directories {
folders_to_check.push(id.to_string());
folders_to_check.push(id.clone());
}
self.information.number_of_checked_folders += folders_to_check.len();
let mut current_folder: String;
let mut next_folder: String;
while !folders_to_check.is_empty() {
if rx.is_some() && rx.unwrap().try_recv().is_ok() {
return false;
}
current_folder = folders_to_check.pop().unwrap();
#[cfg(target_family = "windows")]
{
current_folder = Common::prettier_windows_path(&current_folder);
}
let current_folder = folders_to_check.pop().unwrap();
// Read current dir, if permission are denied just go to next
let read_dir = match fs::read_dir(&current_folder) {
Ok(t) => t,
Err(_) => {
self.text_messages.warnings.push("Cannot open dir ".to_string() + current_folder.as_str());
self.text_messages.warnings.push(format!("Cannot open dir {}", current_folder.display()));
continue;
} // Permissions denied
};
@ -146,14 +141,14 @@ impl Temporary {
let entry_data = match entry {
Ok(t) => t,
Err(_) => {
self.text_messages.warnings.push("Cannot read entry in dir ".to_string() + current_folder.as_str());
self.text_messages.warnings.push(format!("Cannot read entry in dir {}", current_folder.display()));
continue;
} //Permissions denied
};
let metadata: Metadata = match entry_data.metadata() {
Ok(t) => t,
Err(_) => {
self.text_messages.warnings.push("Cannot read metadata in dir ".to_string() + current_folder.as_str());
self.text_messages.warnings.push(format!("Cannot read metadata in dir {}", current_folder.display()));
continue;
} //Permissions denied
};
@ -164,24 +159,11 @@ impl Temporary {
continue;
}
next_folder = "".to_owned()
+ &current_folder
+ match &entry_data.file_name().into_string() {
Ok(t) => t,
Err(_) => continue,
}
+ "/";
let next_folder = current_folder.join(entry_data.file_name());
if self.directories.is_excluded(&next_folder) || self.excluded_items.is_excluded(&next_folder) {
continue 'dir;
}
for ed in &self.directories.excluded_directories {
if next_folder == *ed {
continue 'dir;
}
}
for expression in &self.excluded_items.items {
if Common::regex_check(expression, &next_folder) {
continue 'dir;
}
}
folders_to_check.push(next_folder);
} else if metadata.is_file() {
let file_name_lowercase: String = match entry_data.file_name().into_string() {
@ -197,26 +179,10 @@ impl Temporary {
self.information.number_of_ignored_files += 1;
continue 'dir;
}
// Checking files
#[allow(unused_mut)] // Used is later by Windows build
let mut current_file_name = "".to_owned()
+ &current_folder
+ match &entry_data.file_name().into_string() {
Ok(t) => t,
Err(_) => continue,
};
// Checking expressions
for expression in &self.excluded_items.items {
if Common::regex_check(expression, &current_file_name) {
continue 'dir;
}
}
#[cfg(target_family = "windows")]
{
current_file_name = Common::prettier_windows_path(&current_file_name);
let current_file_name = current_folder.join(entry_data.file_name());
if self.excluded_items.is_excluded(&current_file_name) {
continue 'dir;
}
// Creating new file entry
@ -226,12 +192,12 @@ impl Temporary {
Ok(t) => match t.duration_since(UNIX_EPOCH) {
Ok(d) => d.as_secs(),
Err(_) => {
self.text_messages.warnings.push(format!("File {} seems to be modified before Unix Epoch.", current_file_name));
self.text_messages.warnings.push(format!("File {} seems to be modified before Unix Epoch.", current_file_name.display()));
0
}
},
Err(_) => {
self.text_messages.warnings.push("Unable to get modification date from file ".to_string() + current_file_name.as_str());
self.text_messages.warnings.push(format!("Unable to get modification date from file {}", current_file_name.display()));
continue;
} // Permissions Denied
},
@ -261,7 +227,7 @@ impl Temporary {
DeleteMethod::Delete => {
for file_entry in &self.temporary_files {
if fs::remove_file(file_entry.path.clone()).is_err() {
self.text_messages.warnings.push(file_entry.path.clone());
self.text_messages.warnings.push(file_entry.path.display().to_string());
}
}
}
@ -327,27 +293,24 @@ impl SaveResults for Temporary {
}
};
match file.write_all(
format!(
"Results of searching {:?} with excluded directories {:?} and excluded items {:?}\n",
self.directories.included_directories, self.directories.excluded_directories, self.excluded_items.items
)
.as_bytes(),
) {
Ok(_) => (),
Err(_) => {
self.text_messages.errors.push(format!("Failed to save results to file {}", file_name));
return false;
}
if writeln!(
file,
"Results of searching {:?} with excluded directories {:?} and excluded items {:?}",
self.directories.included_directories, self.directories.excluded_directories, self.excluded_items.items
)
.is_err()
{
self.text_messages.errors.push(format!("Failed to save results to file {}", file_name));
return false;
}
if !self.temporary_files.is_empty() {
file.write_all(format!("Found {} temporary files.\n", self.information.number_of_temporary_files).as_bytes()).unwrap();
writeln!(file, "Found {} temporary files.", self.information.number_of_temporary_files).unwrap();
for file_entry in self.temporary_files.iter() {
file.write_all(format!("{} \n", file_entry.path).as_bytes()).unwrap();
writeln!(file, "{}", file_entry.path.display()).unwrap();
}
} else {
file.write_all(b"Not found any temporary files.").unwrap();
write!(file, "Not found any temporary files.").unwrap();
}
Common::print_time(start_time, SystemTime::now(), "save_results_to_file".to_string());
true
@ -358,7 +321,7 @@ impl PrintResults for Temporary {
let start_time: SystemTime = SystemTime::now();
println!("Found {} temporary files.\n", self.information.number_of_temporary_files);
for file_entry in self.temporary_files.iter() {
println!("{}", file_entry.path);
println!("{}", file_entry.path.display());
}
Common::print_time(start_time, SystemTime::now(), "print_entries".to_string());

View File

@ -19,9 +19,18 @@ use gtk::{Builder, SelectionMode, TreeIter, TreeView};
use std::cell::RefCell;
use std::collections::HashMap;
use std::fs::Metadata;
use std::path::Path;
use std::rc::Rc;
use std::{env, fs, process, thread};
fn split_path(path: &Path) -> (String, String) {
match (path.parent(), path.file_name()) {
(Some(dir), Some(file)) => (dir.display().to_string(), file.to_string_lossy().into_owned()),
(Some(dir), None) => (dir.display().to_string(), String::new()),
(None, _) => (String::new(), String::new()),
}
}
fn main() {
let mut exit_program_after_initialization: bool = false;
// Printing version
@ -1395,12 +1404,10 @@ fn main() {
];
list_store.set(&list_store.append(), &col_indices, &values);
for entry in vector {
let path = &entry.path;
let index = path.rfind('/').unwrap();
let (directory, file) = split_path(&entry.path);
let values: [&dyn ToValue; 6] = [
&(path[index + 1..].to_string()),
&(path[..index].to_string()),
&file,
&directory,
&(NaiveDateTime::from_timestamp(entry.modified_date as i64, 0).to_string()),
&(entry.modified_date),
&(MAIN_ROW_COLOR.to_string()),
@ -1425,12 +1432,10 @@ fn main() {
];
list_store.set(&list_store.append(), &col_indices, &values);
for entry in vector {
let path = &entry.path;
let index = path.rfind('/').unwrap();
let (directory, file) = split_path(&entry.path);
let values: [&dyn ToValue; 6] = [
&(path[index + 1..].to_string()),
&(path[..index].to_string()),
&file,
&directory,
&(NaiveDateTime::from_timestamp(entry.modified_date as i64, 0).to_string()),
&(entry.modified_date),
&(MAIN_ROW_COLOR.to_string()),
@ -1509,10 +1514,9 @@ fn main() {
let hashmap = ef.get_empty_folder_list();
for (name, entry) in hashmap {
let name: String = name[..(name.len() - 1)].to_string();
let index = name.rfind('/').unwrap();
let values: [&dyn ToValue; 3] = [&(name[index + 1..].to_string()), &(name[..index].to_string()), &(NaiveDateTime::from_timestamp(entry.modified_date as i64, 0).to_string())];
for (path, entry) in hashmap {
let (directory, file) = split_path(path);
let values: [&dyn ToValue; 3] = [&file, &directory, &(NaiveDateTime::from_timestamp(entry.modified_date as i64, 0).to_string())];
list_store.set(&list_store.append(), &col_indices, &values);
}
print_text_messages_to_text_view(text_messages, &text_view_errors);
@ -1578,9 +1582,8 @@ fn main() {
let vector = vf.get_empty_files();
for file_entry in vector {
let name: String = file_entry.path.to_string();
let index = name.rfind('/').unwrap();
let values: [&dyn ToValue; 3] = [&(name[index + 1..].to_string()), &(name[..index].to_string()), &(NaiveDateTime::from_timestamp(file_entry.modified_date as i64, 0).to_string())];
let (directory, file) = split_path(&file_entry.path);
let values: [&dyn ToValue; 3] = [&file, &directory, &(NaiveDateTime::from_timestamp(file_entry.modified_date as i64, 0).to_string())];
list_store.set(&list_store.append(), &col_indices, &values);
}
print_text_messages_to_text_view(text_messages, &text_view_errors);
@ -1647,12 +1650,11 @@ fn main() {
for (size, vector) in btreemap.iter().rev() {
for file_entry in vector {
let name: String = file_entry.path.to_string();
let index = name.rfind('/').unwrap();
let (directory, file) = split_path(&file_entry.path);
let values: [&dyn ToValue; 4] = [
&(format!("{} ({} bytes)", size.file_size(options::BINARY).unwrap(), size)),
&(name[index + 1..].to_string()),
&(name[..index].to_string()),
&file,
&directory,
&(NaiveDateTime::from_timestamp(file_entry.modified_date as i64, 0).to_string()),
];
list_store.set(&list_store.append(), &col_indices, &values);
@ -1721,9 +1723,8 @@ fn main() {
let vector = tf.get_temporary_files();
for file_entry in vector {
let name: String = file_entry.path.to_string();
let index = name.rfind('/').unwrap();
let values: [&dyn ToValue; 3] = [&(name[index + 1..].to_string()), &(name[..index].to_string()), &(NaiveDateTime::from_timestamp(file_entry.modified_date as i64, 0).to_string())];
let (directory, file) = split_path(&file_entry.path);
let values: [&dyn ToValue; 3] = [&file, &directory, &(NaiveDateTime::from_timestamp(file_entry.modified_date as i64, 0).to_string())];
list_store.set(&list_store.append(), &col_indices, &values);
}
print_text_messages_to_text_view(text_messages, &text_view_errors);