2020-10-15 05:41:37 +13:00
use std ::ffi ::OsString ;
2020-09-12 23:25:23 +12:00
use std ::fs ;
2022-01-06 10:47:27 +13:00
use std ::fs ::{ File , OpenOptions } ;
2022-01-01 10:34:24 +13:00
use std ::io ::BufReader ;
2020-10-15 05:41:37 +13:00
use std ::path ::{ Path , PathBuf } ;
2020-09-01 05:37:30 +12:00
use std ::time ::SystemTime ;
2022-07-25 06:48:02 +12:00
#[ cfg(feature = " heif " ) ]
use anyhow ::Result ;
2022-06-01 03:52:55 +12:00
use directories_next ::ProjectDirs ;
use image ::{ DynamicImage , ImageBuffer , Rgb } ;
use imagepipe ::{ ImageSource , Pipeline } ;
2022-06-09 07:42:51 +12:00
#[ cfg(feature = " heif " ) ]
2023-03-06 08:54:02 +13:00
use libheif_rs ::{ ColorSpace , HeifContext , RgbChroma } ;
2022-06-09 07:42:51 +12:00
2022-11-26 08:38:27 +13:00
static NUMBER_OF_THREADS : state ::Storage < usize > = state ::Storage ::new ( ) ;
pub fn get_number_of_threads ( ) -> usize {
let data = NUMBER_OF_THREADS . get ( ) ;
if * data > = 1 {
* data
} else {
num_cpus ::get ( )
}
}
pub fn set_default_number_of_threads ( ) {
set_number_of_threads ( num_cpus ::get ( ) ) ;
}
2023-01-29 06:54:02 +13:00
#[ must_use ]
2022-11-26 08:38:27 +13:00
pub fn get_default_number_of_threads ( ) -> usize {
num_cpus ::get ( )
}
pub fn set_number_of_threads ( thread_number : usize ) {
NUMBER_OF_THREADS . set ( thread_number ) ;
rayon ::ThreadPoolBuilder ::new ( ) . num_threads ( get_number_of_threads ( ) ) . build_global ( ) . unwrap ( ) ;
}
2020-09-02 05:34:39 +12:00
/// Class for common functions used across other class/functions
2022-05-17 04:23:07 +12:00
pub const RAW_IMAGE_EXTENSIONS : & [ & str ] = & [
" .mrw " , " .arw " , " .srf " , " .sr2 " , " .mef " , " .orf " , " .srw " , " .erf " , " .kdc " , " .kdc " , " .dcs " , " .rw2 " , " .raf " , " .dcr " , " .dng " , " .pef " , " .crw " , " .iiq " , " .3fr " , " .nrw " , " .nef " , " .mos " ,
" .cr2 " , " .ari " ,
] ;
pub const IMAGE_RS_EXTENSIONS : & [ & str ] = & [
2022-06-09 07:42:51 +12:00
" .jpg " , " .jpeg " , " .png " , " .bmp " , " .tiff " , " .tif " , " .tga " , " .ff " , " .jif " , " .jfi " , " .webp " , " .gif " , " .ico " , " .exr " ,
2022-05-17 04:23:07 +12:00
] ;
2022-06-09 07:42:51 +12:00
pub const IMAGE_RS_SIMILAR_IMAGES_EXTENSIONS : & [ & str ] = & [ " .jpg " , " .jpeg " , " .png " , " .tiff " , " .tif " , " .tga " , " .ff " , " .jif " , " .jfi " , " .bmp " , " .webp " , " .exr " ] ;
2022-05-17 04:23:07 +12:00
pub const IMAGE_RS_BROKEN_FILES_EXTENSIONS : & [ & str ] = & [
2022-06-09 07:42:51 +12:00
" .jpg " , " .jpeg " , " .png " , " .tiff " , " .tif " , " .tga " , " .ff " , " .jif " , " .jfi " , " .gif " , " .bmp " , " .ico " , " .jfif " , " .jpe " , " .pnz " , " .dib " , " .webp " , " .exr " ,
2022-05-17 04:23:07 +12:00
] ;
2022-06-09 07:42:51 +12:00
pub const HEIC_EXTENSIONS : & [ & str ] = & [ " .heif " , " .heifs " , " .heic " , " .heics " , " .avci " , " .avcs " , " .avif " , " .avifs " ] ;
2022-05-17 04:23:07 +12:00
pub const ZIP_FILES_EXTENSIONS : & [ & str ] = & [ " .zip " ] ;
pub const PDF_FILES_EXTENSIONS : & [ & str ] = & [ " .pdf " ] ;
pub const AUDIO_FILES_EXTENSIONS : & [ & str ] = & [
2022-05-31 02:26:27 +12:00
" .mp3 " , " .flac " , " .wav " , " .ogg " , " .m4a " , " .aac " , " .aiff " , " .pcm " , " .aif " , " .aiff " , " .aifc " , " .m3a " , " .mp2 " , " .mp4a " , " .mp2a " , " .mpga " , " .wave " , " .weba " , " .wma " , " .oga " ,
2022-05-17 04:23:07 +12:00
] ;
pub const VIDEO_FILES_EXTENSIONS : & [ & str ] = & [
" .mp4 " , " .mpv " , " .flv " , " .mp4a " , " .webm " , " .mpg " , " .mp2 " , " .mpeg " , " .m4p " , " .m4v " , " .avi " , " .wmv " , " .qt " , " .mov " , " .swf " , " .mkv " ,
] ;
2020-09-02 05:34:39 +12:00
2022-01-14 18:34:43 +13:00
pub const LOOP_DURATION : u32 = 200 ; //ms
2020-09-01 05:37:30 +12:00
pub struct Common ( ) ;
2021-11-28 08:57:10 +13:00
2022-01-06 10:47:27 +13:00
pub fn open_cache_folder ( cache_file_name : & str , save_to_cache : bool , use_json : bool , warnings : & mut Vec < String > ) -> Option < ( ( Option < File > , PathBuf ) , ( Option < File > , PathBuf ) ) > {
if let Some ( proj_dirs ) = ProjectDirs ::from ( " pl " , " Qarmin " , " Czkawka " ) {
let cache_dir = PathBuf ::from ( proj_dirs . cache_dir ( ) ) ;
let cache_file = cache_dir . join ( cache_file_name ) ;
let cache_file_json = cache_dir . join ( cache_file_name . replace ( " .bin " , " .json " ) ) ;
let mut file_handler_default = None ;
let mut file_handler_json = None ;
if save_to_cache {
if cache_dir . exists ( ) {
if ! cache_dir . is_dir ( ) {
warnings . push ( format! ( " Config dir {} is a file! " , cache_dir . display ( ) ) ) ;
return None ;
}
} else if let Err ( e ) = fs ::create_dir_all ( & cache_dir ) {
warnings . push ( format! ( " Cannot create config dir {} , reason {} " , cache_dir . display ( ) , e ) ) ;
return None ;
}
file_handler_default = Some ( match OpenOptions ::new ( ) . truncate ( true ) . write ( true ) . create ( true ) . open ( & cache_file ) {
Ok ( t ) = > t ,
Err ( e ) = > {
warnings . push ( format! ( " Cannot create or open cache file {} , reason {} " , cache_file . display ( ) , e ) ) ;
return None ;
}
} ) ;
if use_json {
file_handler_json = Some ( match OpenOptions ::new ( ) . truncate ( true ) . write ( true ) . create ( true ) . open ( & cache_file_json ) {
Ok ( t ) = > t ,
Err ( e ) = > {
warnings . push ( format! ( " Cannot create or open cache file {} , reason {} " , cache_file_json . display ( ) , e ) ) ;
return None ;
}
} ) ;
}
} else {
if let Ok ( t ) = OpenOptions ::new ( ) . read ( true ) . open ( & cache_file ) {
file_handler_default = Some ( t ) ;
} else {
if use_json {
file_handler_json = Some ( match OpenOptions ::new ( ) . read ( true ) . open ( & cache_file_json ) {
Ok ( t ) = > t ,
Err ( _ ) = > return None ,
} ) ;
} else {
// messages.push(format!("Cannot find or open cache file {}", cache_file.display())); // No error or warning
return None ;
}
}
} ;
return Some ( ( ( file_handler_default , cache_file ) , ( file_handler_json , cache_file_json ) ) ) ;
}
None
}
2022-06-09 07:42:51 +12:00
#[ cfg(feature = " heif " ) ]
pub fn get_dynamic_image_from_heic ( path : & str ) -> Result < DynamicImage > {
let im = HeifContext ::read_from_file ( path ) ? ;
let handle = im . primary_image_handle ( ) ? ;
2023-02-19 22:21:14 +13:00
let image = handle . decode ( ColorSpace ::Rgb ( RgbChroma ::Rgb ) , None ) ? ;
2023-03-06 08:54:02 +13:00
let width = image . width ( ) ;
let height = image . height ( ) ;
2022-06-09 07:42:51 +12:00
let planes = image . planes ( ) ;
let interleaved_plane = planes . interleaved . unwrap ( ) ;
ImageBuffer ::from_raw ( width , height , interleaved_plane . data . to_owned ( ) )
. map ( DynamicImage ::ImageRgb8 )
. ok_or_else ( | | anyhow ::anyhow! ( " Failed to create image buffer " ) )
}
2022-01-01 10:34:24 +13:00
pub fn get_dynamic_image_from_raw_image ( path : impl AsRef < Path > + std ::fmt ::Debug ) -> Option < DynamicImage > {
let file_handler = match OpenOptions ::new ( ) . read ( true ) . open ( & path ) {
Ok ( t ) = > t ,
Err ( _e ) = > {
return None ;
}
} ;
let mut reader = BufReader ::new ( file_handler ) ;
let raw = match rawloader ::decode ( & mut reader ) {
Ok ( raw ) = > raw ,
Err ( _e ) = > {
return None ;
}
} ;
let source = ImageSource ::Raw ( raw ) ;
2022-05-17 04:23:07 +12:00
let mut pipeline = match Pipeline ::new_from_source ( source ) {
2022-01-01 10:34:24 +13:00
Ok ( pipeline ) = > pipeline ,
Err ( _e ) = > {
return None ;
}
} ;
pipeline . run ( None ) ;
let image = match pipeline . output_8bit ( None ) {
Ok ( image ) = > image ,
Err ( _e ) = > {
return None ;
}
} ;
2023-03-06 08:54:02 +13:00
let Some ( image ) = ImageBuffer ::< Rgb < u8 > , Vec < u8 > > ::from_raw ( image . width as u32 , image . height as u32 , image . data ) else {
2022-01-01 10:34:24 +13:00
return None ;
} ;
// println!("Properly hashed {:?}", path);
2022-06-01 03:52:55 +12:00
Some ( DynamicImage ::ImageRgb8 ( image ) )
2022-01-01 10:34:24 +13:00
}
2023-01-29 06:54:02 +13:00
#[ must_use ]
2022-06-05 07:20:21 +12:00
pub 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 ( ) ) ,
}
}
2023-01-29 06:54:02 +13:00
#[ must_use ]
2022-07-28 17:29:50 +12:00
pub fn create_crash_message ( library_name : & str , file_path : & str , home_library_url : & str ) -> String {
format! ( " {library_name} library crashed when opening \" {file_path} \" , please check if this is fixed with the latest version of {library_name} (e.g. with https://github.com/qarmin/crates_tester) and if it is not fixed, please report bug here - {home_library_url} " )
}
2020-09-01 05:37:30 +12:00
impl Common {
2020-09-12 23:25:23 +12:00
/// Printing time which took between start and stop point and prints also function name
2020-10-10 09:32:08 +13:00
#[ allow(unused_variables) ]
2023-01-29 06:54:02 +13:00
pub fn print_time ( start_time : SystemTime , end_time : SystemTime , function_name : & str ) {
2020-09-17 22:07:58 +12:00
#[ cfg(debug_assertions) ]
2021-12-22 06:44:20 +13:00
println! (
" Execution of function \" {} \" took {:?} " ,
function_name ,
end_time . duration_since ( start_time ) . expect ( " Time cannot go reverse. " )
) ;
2020-09-01 05:37:30 +12:00
}
2020-09-12 08:32:17 +12:00
2023-01-29 06:54:02 +13:00
#[ must_use ]
2020-09-12 23:25:23 +12:00
pub fn delete_multiple_entries ( entries : & [ String ] ) -> Vec < String > {
let mut path : & Path ;
let mut warnings : Vec < String > = Vec ::new ( ) ;
for entry in entries {
path = Path ::new ( entry ) ;
if path . is_dir ( ) {
2022-11-24 08:23:17 +13:00
if let Err ( e ) = fs ::remove_dir_all ( entry ) {
2022-12-21 20:44:26 +13:00
warnings . push ( format! ( " Failed to remove folder {entry} , reason {e} " ) ) ;
2020-09-12 23:25:23 +12:00
}
2022-11-24 08:23:17 +13:00
} else if let Err ( e ) = fs ::remove_file ( entry ) {
2022-12-21 20:44:26 +13:00
warnings . push ( format! ( " Failed to remove file {entry} , reason {e} " ) ) ;
2020-09-12 23:25:23 +12:00
}
}
warnings
}
2023-01-29 06:54:02 +13:00
#[ must_use ]
2020-09-12 23:25:23 +12:00
pub fn delete_one_entry ( entry : & str ) -> String {
let path : & Path = Path ::new ( entry ) ;
2023-01-29 06:54:02 +13:00
let mut warning : String = String ::new ( ) ;
2020-09-16 05:17:13 +12:00
if path . is_dir ( ) {
2022-11-24 08:23:17 +13:00
if let Err ( e ) = fs ::remove_dir_all ( entry ) {
2023-01-29 06:54:02 +13:00
warning = format! ( " Failed to remove folder {entry} , reason {e} " ) ;
2020-09-16 05:17:13 +12:00
}
2022-11-24 08:23:17 +13:00
} else if let Err ( e ) = fs ::remove_file ( entry ) {
2023-01-29 06:54:02 +13:00
warning = format! ( " Failed to remove file {entry} , reason {e} " ) ;
2020-09-12 23:25:23 +12:00
}
warning
}
2020-09-12 08:32:17 +12:00
/// Function to check if directory match expression
2020-10-15 05:41:37 +13:00
pub fn regex_check ( expression : & str , directory : impl AsRef < Path > ) -> bool {
2021-12-03 01:31:10 +13:00
if expression = = " * " {
return true ;
}
2020-09-12 08:32:17 +12:00
let temp_splits : Vec < & str > = expression . split ( '*' ) . collect ( ) ;
let mut splits : Vec < & str > = Vec ::new ( ) ;
for i in temp_splits {
2021-01-01 07:53:49 +13:00
if ! i . is_empty ( ) {
2020-09-12 08:32:17 +12:00
splits . push ( i ) ;
}
}
if splits . is_empty ( ) {
return false ;
}
2020-10-15 05:41:37 +13:00
// Get rid of non unicode characters
let directory = directory . as_ref ( ) . to_string_lossy ( ) ;
2020-09-12 08:32:17 +12:00
// Early checking if directory contains all parts needed by expression
for split in & splits {
if ! directory . contains ( split ) {
return false ;
}
}
let mut position_of_splits : Vec < usize > = Vec ::new ( ) ;
// `git*` shouldn't be true for `/gitsfafasfs`
2022-11-24 08:23:17 +13:00
if ! expression . starts_with ( '*' ) & & directory . find ( splits [ 0 ] ) . unwrap ( ) > 0 {
2020-09-12 08:32:17 +12:00
return false ;
}
// `*home` shouldn't be true for `/homeowner`
if ! expression . ends_with ( '*' ) & & ! directory . ends_with ( splits . last ( ) . unwrap ( ) ) {
return false ;
}
// At the end we check if parts between * are correctly positioned
2022-11-24 08:23:17 +13:00
position_of_splits . push ( directory . find ( splits [ 0 ] ) . unwrap ( ) ) ;
2020-09-12 08:32:17 +12:00
let mut current_index : usize ;
let mut found_index : usize ;
for i in splits [ 1 .. ] . iter ( ) . enumerate ( ) {
current_index = * position_of_splits . get ( i . 0 ) . unwrap ( ) + i . 1. len ( ) ;
found_index = match directory [ current_index .. ] . find ( i . 1 ) {
Some ( t ) = > t ,
None = > return false ,
} ;
position_of_splits . push ( found_index + current_index ) ;
}
true
}
2020-10-15 05:41:37 +13:00
pub fn normalize_windows_path ( path_to_change : impl AsRef < Path > ) -> PathBuf {
let path = path_to_change . as_ref ( ) ;
2020-12-31 01:41:18 +13:00
// Don't do anything, because network path may be case intensive
if path . to_string_lossy ( ) . starts_with ( '\\' ) {
return path . to_path_buf ( ) ;
}
2020-10-15 05:41:37 +13:00
match path . to_str ( ) {
Some ( path ) if path . is_char_boundary ( 1 ) = > {
2021-12-19 11:45:37 +13:00
let replaced = path . replace ( '/' , " \\ " ) ;
2020-10-15 05:41:37 +13:00
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 ( ) ,
}
2020-10-11 02:18:04 +13:00
}
2020-09-12 08:32:17 +12:00
}
2020-09-18 17:32:37 +12:00
2020-09-12 08:32:17 +12:00
#[ cfg(test) ]
mod test {
2020-10-15 05:41:37 +13:00
use std ::path ::PathBuf ;
2020-09-12 08:32:17 +12:00
2021-11-28 08:49:20 +13:00
use crate ::common ::Common ;
2020-09-12 08:32:17 +12:00
#[ test ]
fn test_regex ( ) {
assert! ( Common ::regex_check ( " *home* " , " /home/rafal " ) ) ;
assert! ( Common ::regex_check ( " *home " , " /home " ) ) ;
assert! ( Common ::regex_check ( " *home/ " , " /home/ " ) ) ;
assert! ( Common ::regex_check ( " *home/* " , " /home/ " ) ) ;
assert! ( Common ::regex_check ( " *.git* " , " /home/.git " ) ) ;
assert! ( Common ::regex_check ( " */home/rafal*rafal*rafal*rafal* " , " /home/rafal/rafalrafalrafal " ) ) ;
2020-12-15 01:07:35 +13:00
assert! ( Common ::regex_check ( " AAA " , " AAA " ) ) ;
assert! ( Common ::regex_check ( " AAA* " , " AAABDGG/QQPW* " ) ) ;
2020-09-12 08:32:17 +12:00
assert! ( ! Common ::regex_check ( " *home " , " /home/ " ) ) ;
assert! ( ! Common ::regex_check ( " *home " , " /homefasfasfasfasf/ " ) ) ;
assert! ( ! Common ::regex_check ( " *home " , " /homefasfasfasfasf " ) ) ;
assert! ( ! Common ::regex_check ( " rafal*afal*fal " , " rafal " ) ) ;
2020-09-17 23:35:11 +12:00
assert! ( ! Common ::regex_check ( " rafal*a " , " rafal " ) ) ;
2020-09-12 08:32:17 +12:00
assert! ( ! Common ::regex_check ( " AAAAAAAA**** " , " /AAAAAAAAAAAAAAAAA " ) ) ;
assert! ( ! Common ::regex_check ( " *.git/* " , " /home/.git " ) ) ;
assert! ( ! Common ::regex_check ( " *home/*koc " , " /koc/home/ " ) ) ;
assert! ( ! Common ::regex_check ( " *home/ " , " /home " ) ) ;
assert! ( ! Common ::regex_check ( " *TTT " , " /GGG " ) ) ;
2021-05-09 07:54:01 +12:00
#[ cfg(target_family = " windows " ) ]
{
assert! ( Common ::regex_check ( " * \\ home " , " C: \\ home " ) ) ;
assert! ( Common ::regex_check ( " */home " , " C: \\ home " ) ) ;
}
2020-09-12 08:32:17 +12:00
}
2021-11-28 08:57:10 +13:00
2020-10-11 02:18:04 +13:00
#[ test ]
fn test_windows_path ( ) {
2020-12-22 06:22:59 +13:00
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 " ) ) ;
2020-12-31 01:41:18 +13:00
assert_eq! ( PathBuf ::from ( " \\ \\ aBBa " ) , Common ::normalize_windows_path ( " \\ \\ aBBa " ) ) ;
assert_eq! ( PathBuf ::from ( " a " ) , Common ::normalize_windows_path ( " a " ) ) ;
assert_eq! ( PathBuf ::from ( " " ) , Common ::normalize_windows_path ( " " ) ) ;
2020-10-11 02:18:04 +13:00
}
2020-09-01 05:37:30 +12:00
}