backup_deduplicator/data/path.rs
1use std::ffi::OsString;
2use std::fmt::Formatter;
3use std::path::PathBuf;
4use anyhow::{Result};
5use serde::{Deserialize, Serialize};
6
7/// The type of archive.
8#[derive(Debug, Clone, Serialize, Deserialize, Hash)]
9pub enum ArchiveType {
10 Tar,
11 Zip,
12}
13
14/// The target of a path.
15///
16/// # Fields
17/// * `File` - The path points to a file.
18/// * `Archive` - The path points to an archive. That is further traversed.
19#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
20pub enum PathTarget {
21 File,
22 // Archive(ArchiveType),
23}
24
25/// A path component. A path points to a file or an archive.
26///
27/// # Fields
28/// * `path` - The path.
29/// * `target` - The target of the path.
30#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
31pub struct PathComponent {
32 pub path: PathBuf,
33 pub target: PathTarget,
34}
35
36/// A file path. A file path specifies a target file. It may consist of multiple path components.
37/// Imagine the following file structure:
38///
39/// ```text
40/// DIR stuff
41/// \-- DIR more_stuff
42/// \-- FILE archive.tar.gz
43/// \-- FILE file_in_archive.txt
44/// ```
45///
46/// The file path to `file_in_archive.txt` would consist of the following path components:
47/// - `stuff/more_stuff/archive.tar.gz` (target: Archive)
48/// - `file_in_archive.txt` (target: File)
49///
50/// The file path to `archive.tar.gz` would consist of the following path components:
51/// - `stuff/more_stuff/archive.tar.gz` (target: File)
52///
53/// # Fields
54/// * `path` - The path components.
55///
56/// # Examples
57/// ```
58/// use std::path::PathBuf;
59/// use backup_deduplicator::path::FilePath;
60///
61/// let path = FilePath::from_realpath(PathBuf::from("test.txt"));
62///
63/// ```
64#[derive(Debug, Clone, Serialize, Deserialize, Hash)]
65pub struct FilePath {
66 pub path: Vec<PathComponent>
67}
68
69impl FilePath {
70 /// Creates a new file path from path components.
71 ///
72 /// # Arguments
73 /// * `path` - The path components.
74 ///
75 /// # Returns
76 /// The file path.
77 pub fn from_pathcomponents(path: Vec<PathComponent>) -> Self {
78 FilePath {
79 path
80 }
81 }
82
83 /// Creates a new file path from a real path.
84 ///
85 /// # Arguments
86 /// * `path` - The real path.
87 ///
88 /// # Returns
89 /// The file path.
90 pub fn from_realpath(path: PathBuf) -> Self {
91 FilePath {
92 path: vec![PathComponent {
93 path,
94 target: PathTarget::File
95 }]
96 }
97 }
98
99 pub fn join_realpath(&mut self, _path: PathBuf) {
100 todo!("implement")
101 }
102
103 pub fn extract_parent(&self, _temp_directory: &PathBuf) {
104 todo!("implement")
105 }
106
107 pub fn delete_parent(&self, _temp_directory: &PathBuf) {
108 todo!("implement")
109 }
110
111 /// Resolves the file path to a single file.
112 ///
113 /// # Returns
114 /// The resolved file path.
115 ///
116 /// # Errors
117 /// Never
118 pub fn resolve_file(&self) -> Result<PathBuf> {
119 if self.path.len() == 1 {
120 match self.path[0].target {
121 PathTarget::File => Ok(self.path[0].path.clone()),
122 }
123 } else {
124 todo!("implement")
125 }
126 }
127
128 /// Gets the child of where the file path points to.
129 ///
130 /// # Arguments
131 /// * `child_name` - The name of the child.
132 ///
133 /// # Returns
134 /// The child file path.
135 ///
136 /// # Example
137 /// ```
138 /// use std::path::PathBuf;
139 /// use backup_deduplicator::path::FilePath;
140 ///
141 /// let path = FilePath::from_realpath(PathBuf::from("test/"));
142 /// let child = path.child("child.txt");
143 ///
144 /// assert_eq!(child.path[0].path, PathBuf::from("test/child.txt"));
145 /// assert_eq!(child.path.len(), 1);
146 /// ```
147 ///
148 /// ```
149 /// use std::path::PathBuf;
150 /// use backup_deduplicator::path::FilePath;
151 ///
152 /// let path = FilePath::from_realpath(PathBuf::from("test/"));
153 /// let subpath = path.child("subdir").child("abc.txt");
154 ///
155 /// assert_eq!(subpath.path[0].path, PathBuf::from("test/subdir/abc.txt"));
156 /// assert_eq!(subpath.path.len(), 1);
157 /// ```
158 pub fn child<Str: Into<OsString>>(&self, child_name: Str) -> FilePath {
159 let mut result = FilePath {
160 path: self.path.clone()
161 };
162
163 let component = PathBuf::from(child_name.into());
164
165 match result.path.last_mut() {
166 Some(last) => {
167 last.path.push(component);
168 },
169 None => {
170 result.path.push(PathComponent {
171 path: component,
172 target: PathTarget::File
173 });
174 }
175 }
176
177 return result;
178 }
179
180 /// Gets the parent of the file path.
181 ///
182 /// # Returns
183 /// The parent file path. None if the file path has no parent.
184 ///
185 /// # Example
186 /// ```
187 /// use std::path::PathBuf;
188 /// use backup_deduplicator::path::FilePath;
189 ///
190 /// let path = FilePath::from_realpath(PathBuf::from("test/abc/def.txt"));
191 /// let parent = path.parent().unwrap();
192 ///
193 /// assert_eq!(parent.path[0].path, PathBuf::from("test/abc"));
194 ///
195 /// // test/abc test/ "" None
196 /// let root = path.parent().unwrap().parent().unwrap().parent().unwrap().parent();
197 ///
198 /// assert_eq!(root, None);
199 /// ```
200 pub fn parent(&self) -> Option<FilePath> {
201 let last = self.path.last();
202
203 match last {
204 None => None,
205 Some(last) => {
206 let parent = last.path.parent();
207
208 match parent {
209 Some(parent) => {
210 let mut result = FilePath {
211 path: self.path.clone()
212 };
213 let last = result.path.last_mut().unwrap();
214 last.path = parent.to_path_buf();
215
216 Some(result)
217 },
218 None => {
219 if self.path.len() == 1 {
220 None
221 } else {
222 let mut result = FilePath {
223 path: self.path.clone()
224 };
225 result.path.pop();
226 Some(result)
227 }
228 }
229 }
230 }
231 }
232 }
233}
234
235impl PartialEq for FilePath {
236 /// Compares two file paths.
237 ///
238 /// # Arguments
239 /// * `other` - The other file path.
240 ///
241 /// # Returns
242 /// Whether the file paths are equal.
243 fn eq(&self, other: &Self) -> bool {
244 self.path.len() == other.path.len() && self.path.iter().zip(other.path.iter()).all(|(a, b)| a == b)
245 }
246}
247
248impl Eq for FilePath {}
249
250impl std::fmt::Display for FilePath {
251 /// Formats the file path to a string.
252 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
253 let mut result = String::new();
254
255 let mut first = true;
256 for component in &self.path {
257 if first {
258 first = false;
259 } else {
260 result.push_str("| ");
261 }
262
263 result.push_str(component.path.to_str().unwrap_or_else(|| "<invalid path>"));
264 }
265
266 write!(f, "{}", result)
267 }
268}