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}