1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
use std::{
ffi::OsStr,
path::{Path, PathBuf},
};
use crate::types::*;
/// All path-related operations
impl GetPathInfo {
/// Check if paths are files or directories
///
/// ### Return Value
/// Returns a tuple of two booleans `(path1 is file, path2 is file)`
/// * `true` - Path points to a file
/// * `false` - Path points to a directory
pub fn if_file(&self) -> (bool, bool) {
(self.path1.is_file(), self.path2.is_file())
}
/// Detect if there is an inclusion relationship between two paths (parent-child directory issue)
///
/// This method is used to determine if there is an inclusion relationship between two paths,
/// which is crucial for determining the rename order. When two directories have a parent-child
/// relationship, the rename order will directly affect the success of the operation.
///
/// ### Return Value
/// * `1` - path1 contains path2 (path1 is the parent or ancestor directory of path2)
/// * `2` - path2 contains path1 (path2 is the parent or ancestor directory of path1)
/// * `0` - No inclusion relationship
pub fn if_root(&self) -> u8 {
if Self::path_is_parent(&self.path1, &self.path2) {
1 // path1 contains path2
} else if Self::path_is_parent(&self.path2, &self.path1) {
2 // path2 contains path1
} else {
0 // No inclusion relationship
}
}
/// Helper function: Check if there is a parent-child directory relationship
///
/// Determine if potential_parent is the parent or ancestor directory of potential_child
///
/// ### Parameters
/// * `potential_parent` - Potential parent directory path
/// * `potential_child` - Potential child directory path
///
/// ### Return Value
/// * `true` - There is indeed a parent-child relationship
/// * `false` - No parent-child relationship
fn path_is_parent(potential_parent: &Path, potential_child: &Path) -> bool {
// Try to determine the path of child relative to parent
if let Ok(relative) = potential_child.strip_prefix(potential_parent) {
*relative != *""
} else {
false
}
}
/// Get metadata information of file or directory
///
/// Extract the file name (without suffix), extension, and parent directory path
///
/// ### Parameters
/// * `file_path` - File or directory path to process
/// * `is_file` - Indicates if path is a file or directory
///
/// ### Return Value
/// Returns `MetadataCollection` structure containing metadata
fn get_info(file_path: &Path, is_file: bool) -> MetadataCollection {
// Closure function to extract strings, processing file names and extensions
// If processing extension, add leading dot "."
let get_string_closure = |original_result: &Option<&OsStr>, is_ext: bool| {
match original_result {
Some(i) => {
if is_ext {
// Whether calculating suffix, if so, add leading dot "."
".".to_owned() + &i.to_string_lossy()
} else {
i.to_string_lossy().to_string()
}
}
/*
If not available, ignore
Since verification has been completed earlier, if Err occurs here,
it is due to special file naming and does not affect subsequent operations.
e.g. "C:\\.cargo\\.config", this file cannot get suffix, this folder also cannot get suffix
*/
Option::None => String::new(),
}
};
if !is_file {
// Process directory path
MetadataCollection {
name: {
// For directories, name includes stem and extension (if any)
get_string_closure(&file_path.file_stem(), false)
+ get_string_closure(&file_path.extension(), true).as_ref()
},
ext: String::new(), // Directories have no extension
parent_dir: {
match &file_path.parent() {
Some(i) => i.to_path_buf(),
Option::None => PathBuf::new(),
}
},
}
} else {
// Process file path
MetadataCollection {
name: get_string_closure(&file_path.file_stem(), false),
ext: get_string_closure(&file_path.extension(), true),
parent_dir: {
match &file_path.parent() {
Some(i) => i.to_path_buf(),
Option::None => PathBuf::new(),
}
},
}
}
}
/// Collect metadata information of two paths
///
/// ### Parameters
/// * `is_file1` - Indicates if path1 is a file or directory
/// * `is_file2` - Indicates if path2 is a file or directory
///
/// ### Return Value
/// Returns tuple containing two metadata collections `(path1 metadata, path2 metadata)`
pub fn metadata_collect(
&self,
is_file1: bool,
is_file2: bool,
) -> (MetadataCollection, MetadataCollection) {
let metadata1 = GetPathInfo::get_info(&self.path1, is_file1);
let metadata2 = GetPathInfo::get_info(&self.path2, is_file2);
(metadata1, metadata2)
}
}