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