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}