Skip to main content

ex_cli/fs/
file.rs

1use crate::cli::file::FileKind;
2use crate::git::flags::GitFlags;
3use chrono::{DateTime, Utc};
4use std::cmp::Ordering;
5use std::hash::{Hash, Hasher};
6use std::path::{Path, PathBuf};
7use std::rc::Rc;
8use std::time::UNIX_EPOCH;
9
10pub type Signature = [u8; 4];
11
12#[derive(Debug)]
13pub struct File {
14    pub abs_dir: PathBuf,
15    pub rel_dir: PathBuf,
16    pub file_depth: usize,
17    pub inner_depth: Option<usize>,
18    pub file_name: String,
19    pub file_ext: String,
20    pub file_type: FileKind,
21    pub file_mode: u32,
22    pub owner_user: Option<Rc<String>>,
23    pub owner_group: Option<Rc<String>>,
24    pub file_size: u64,
25    pub file_time: DateTime<Utc>,
26    pub git_flags: Option<GitFlags>,
27    pub file_crc: u32,
28    pub file_sig: Signature,
29    #[cfg(windows)]
30    pub file_ver: Option<String>,
31    pub link_data: Option<(PathBuf, FileKind)>,
32}
33
34impl File {
35    pub fn new(
36        abs_dir: PathBuf,
37        rel_dir: PathBuf,
38        file_depth: usize,
39        inner_depth: Option<usize>,
40        file_name: String,
41        file_ext: String,
42        file_type: FileKind,
43    ) -> Self {
44        Self {
45            abs_dir,
46            rel_dir,
47            file_depth,
48            inner_depth,
49            file_name,
50            file_ext,
51            file_type,
52            file_mode: 0,
53            owner_user: None,
54            owner_group: None,
55            file_size: 0,
56            file_time: DateTime::<Utc>::from(UNIX_EPOCH),
57            git_flags: None,
58            file_crc: 0,
59            file_sig: [0; 4],
60            #[cfg(windows)]
61            file_ver: None,
62            link_data: None,
63        }
64    }
65
66    #[cfg(test)]
67    pub fn with_dirs(mut self, abs_dir: PathBuf, rel_dir: PathBuf) -> Self {
68        self.abs_dir = abs_dir;
69        self.rel_dir = rel_dir;
70        self
71    }
72
73    #[cfg(test)]
74    pub fn with_inner_depth(mut self, inner_depth: Option<usize>) -> Self {
75        self.inner_depth = inner_depth;
76        self
77    }
78
79    #[cfg(test)]
80    pub fn with_type(mut self, file_type: FileKind) -> Self {
81        self.file_type = file_type;
82        self
83    }
84
85    pub fn with_mode(mut self, file_mode: u32) -> Self {
86        self.file_mode = file_mode;
87        self
88    }
89
90    pub fn with_owner(
91        mut self,
92        owner_user: Option<Rc<String>>,
93        owner_group: Option<Rc<String>>,
94    ) -> Self {
95        self.owner_user = owner_user;
96        self.owner_group = owner_group;
97        self
98    }
99
100    #[cfg(test)]
101    pub fn with_owner_ref(
102        mut self,
103        owner_user: &str,
104        owner_group: &str,
105    ) -> Self {
106        self.owner_user = Some(Rc::new(String::from(owner_user)));
107        self.owner_group = Some(Rc::new(String::from(owner_group)));
108        self
109    }
110
111    pub fn with_size(mut self, file_size: u64) -> Self {
112        self.file_size = file_size;
113        self
114    }
115
116    pub fn with_time(mut self, file_time: DateTime<Utc>) -> Self {
117        self.file_time = file_time;
118        self
119    }
120
121    #[cfg(test)]
122    pub fn with_date(mut self, year: i32, month: u32, day: u32) -> Self {
123        use crate::util::time::tests::create_date;
124        self.file_time = create_date(year, month, day).unwrap();
125        self
126    }
127
128    pub fn with_git(mut self, git_flags: Option<GitFlags>) -> Self {
129        self.git_flags = git_flags;
130        self
131    }
132
133    pub fn with_crc(mut self, file_crc: u32) -> Self {
134        self.file_crc = file_crc;
135        self
136    }
137
138    pub fn with_sig(mut self, file_sig: Option<Signature>) -> Self {
139        if let Some(file_sig) = file_sig {
140            self.file_sig = file_sig;
141        }
142        self
143    }
144
145    #[cfg(test)]
146    pub fn with_sig_vec(mut self, file_sig: Signature) -> Self {
147        self.file_crc = crc32fast::hash(&file_sig);
148        self.file_sig = file_sig;
149        self
150    }
151
152    #[cfg(test)]
153    pub fn with_sig_str(mut self, file_sig: &str) -> Self {
154        use std::cmp;
155        let file_sig = file_sig.as_bytes();
156        let len = cmp::min(file_sig.len(), 4);
157        self.file_crc = crc32fast::hash(file_sig);
158        self.file_sig[0..len].copy_from_slice(file_sig);
159        self
160    }
161
162    #[cfg(windows)]
163    pub fn with_version(mut self, file_ver: String) -> Self {
164        self.file_ver = Some(file_ver);
165        self
166    }
167
168    pub fn with_link(mut self, link_path: PathBuf, link_type: FileKind) -> Self {
169        self.link_data = Some((link_path, link_type));
170        self
171    }
172
173    pub fn get_path(&self) -> PathBuf {
174        self.abs_dir.join(&self.file_name)
175    }
176
177    pub fn select_dir(&self, abs_path: bool) -> &Path {
178        if abs_path {
179            &self.abs_dir
180        } else {
181            &self.rel_dir
182        }
183    }
184
185    pub fn select_parent_for_indent(&self) -> Option<PathBuf> {
186        if self.file_type == FileKind::Dir {
187            self.rel_dir.parent().map(PathBuf::from)
188        } else {
189            Some(self.rel_dir.clone())
190        }
191    }
192
193    pub fn group_dir_before_file(&self) -> u8 {
194        if self.file_type == FileKind::Dir { 0 } else { 1 }
195    }
196}
197
198impl PartialEq for File {
199    fn eq(&self, other: &Self) -> bool {
200        (self.abs_dir == other.abs_dir) && (self.file_name == other.file_name)
201    }
202}
203
204impl Eq for File {
205}
206
207impl PartialOrd for File {
208    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
209        Some(Ord::cmp(self, other))
210    }
211}
212
213impl Ord for File {
214    fn cmp(&self, other: &Self) -> Ordering {
215        match PathBuf::cmp(&self.abs_dir, &other.abs_dir) {
216            Ordering::Less => Ordering::Less,
217            Ordering::Greater => Ordering::Greater,
218            Ordering::Equal => String::cmp(&self.file_name, &other.file_name),
219        }
220    }
221}
222
223impl Hash for File {
224    fn hash<H: Hasher>(&self, state: &mut H) {
225        self.abs_dir.hash(state);
226        self.file_name.hash(state);
227    }
228}