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