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}