1use crate::error::{MyError, MyResult};
2use crate::fs::file::Signature;
3use crate::fs::flags::FileFlags;
4use crate::fs::metadata::Metadata;
5#[cfg(windows)]
6use crate::util::version;
7use crc32fast::Hasher;
8use std::cell::RefCell;
9use std::ffi::OsStr;
10use std::fs::File;
11use std::io::Read;
12use std::path::{Path, PathBuf};
13use std::time::SystemTime;
14#[cfg(unix)]
15use uzers::{gid_t, uid_t};
16use walkdir::DirEntry;
17
18pub type EntryResult<'a> = MyResult<&'a dyn Entry>;
19
20pub trait Entry {
21 fn file_path(&self) -> &Path;
22
23 fn file_name(&self) -> &OsStr;
24
25 fn file_depth(&self) -> usize;
26
27 fn inner_path(&self) -> Option<&Path>;
28
29 fn inner_depth(&self) -> Option<usize>;
30
31 fn file_flags(&self) -> FileFlags;
32
33 fn read_crc(&self) -> u32;
34
35 fn read_sig(&self) -> Option<Signature>;
36
37 #[cfg(windows)]
38 fn read_version(&self) -> Option<String>;
39
40 fn read_link(&self) -> MyResult<Option<PathBuf>>;
41
42 fn copy_metadata(&self, other: &dyn Entry);
43
44 fn reset_metadata(&self);
45
46 fn file_mode(&self) -> u32;
47
48 #[cfg(unix)]
49 fn owner_uid(&self) -> uid_t;
50
51 #[cfg(unix)]
52 fn owner_gid(&self) -> gid_t;
53
54 fn file_size(&self) -> u64;
55
56 fn file_time(&self) -> SystemTime;
57}
58
59#[derive(Clone)]
60pub struct FileEntry {
61 file_path: PathBuf,
62 file_depth: usize,
63 zip_archive: bool,
64 file_flags: FileFlags,
65 file_metadata: RefCell<Option<Metadata>>,
66}
67
68impl FileEntry {
69 pub fn from_entry(dir_entry: DirEntry, zip_archive: bool) -> Box<dyn Entry> {
70 let file_flags = FileFlags::from_type(dir_entry.file_type(), zip_archive);
71 let file_depth = dir_entry.depth();
72 let file_path = dir_entry.into_path();
73 let file_metadata = RefCell::new(None);
74 let entry = Self {
75 file_path,
76 file_depth,
77 zip_archive,
78 file_flags,
79 file_metadata,
80 };
81 Box::new(entry)
82 }
83
84 pub fn from_path(path: &Path) -> MyResult<Box<dyn Entry>> {
85 let file_path = PathBuf::from(path);
86 let file_metadata = Metadata::from_path(path)?;
87 let file_flags = file_metadata.file_flags;
88 let file_metadata = RefCell::new(Some(file_metadata));
89 let entry = Self {
90 file_path,
91 file_depth: 0,
92 zip_archive: false,
93 file_flags,
94 file_metadata,
95 };
96 Ok(Box::new(entry))
97 }
98
99 #[cfg(test)]
100 pub fn from_fields(
101 file_path: PathBuf,
102 file_depth: usize,
103 file_type: char,
104 file_metadata: Metadata,
105 ) -> Self {
106 let file_flags = FileFlags::from_char(file_type);
107 let file_metadata = RefCell::new(Some(file_metadata));
108 Self {
109 file_path,
110 file_depth,
111 zip_archive: false,
112 file_flags,
113 file_metadata,
114 }
115 }
116
117 #[cfg(test)]
118 pub fn subtract_depth(&self, depth: usize) -> Option<Self> {
119 if self.file_depth >= depth {
120 let mut entry = self.clone();
121 entry.file_depth -= depth;
122 return Some(entry);
123 }
124 None
125 }
126
127 fn get_metadata(&self) -> Metadata {
128 Metadata::from_path(&self.file_path).unwrap_or_default()
129 }
130}
131
132impl Entry for FileEntry {
133 fn file_path(&self) -> &Path {
134 &self.file_path
135 }
136
137 fn file_name(&self) -> &OsStr {
138 self.file_path.file_name().unwrap_or_default()
139 }
140
141 fn file_depth(&self) -> usize {
142 self.file_depth
143 }
144
145 fn inner_path(&self) -> Option<&Path> {
146 None
147 }
148
149 fn inner_depth(&self) -> Option<usize> {
150 self.zip_archive.then_some(0)
151 }
152
153 fn file_flags(&self) -> FileFlags {
154 self.file_flags
155 }
156
157 fn read_crc(&self) -> u32 {
158 crc32_file(&self.file_path).unwrap_or_default()
159 }
160
161 fn read_sig(&self) -> Option<Signature> {
162 let mut data = [0; 4];
163 match File::open(&self.file_path) {
164 Ok(mut file) => file.read(&mut data).map(|_| data).ok(),
165 Err(_) => None,
166 }
167 }
168
169 #[cfg(windows)]
170 fn read_version(&self) -> Option<String> {
171 if version::inner::test_extension(&self.file_path) {
172 version::inner::query_file(&self.file_path)
173 } else {
174 None
175 }
176 }
177
178 fn read_link(&self) -> MyResult<Option<PathBuf>> {
179 match self.file_path.read_link() {
180 Ok(link) => Ok(Some(link)),
181 Err(error) => Err(MyError::from((error, self.file_path.as_path()))),
182 }
183 }
184
185 #[cfg(unix)]
186 fn copy_metadata(&self, other: &dyn Entry) {
187 let mut metadata = self.file_metadata.borrow_mut();
188 let metadata = metadata.get_or_insert_with(|| self.get_metadata());
189 metadata.file_mode = other.file_mode();
190 metadata.owner_uid = other.owner_uid();
191 metadata.owner_gid = other.owner_gid();
192 metadata.file_size = other.file_size();
193 metadata.file_time = other.file_time();
194 }
195
196 #[cfg(not(unix))]
197 fn copy_metadata(&self, other: &dyn Entry) {
198 let mut metadata = self.file_metadata.borrow_mut();
199 let metadata = metadata.get_or_insert_with(|| self.get_metadata());
200 metadata.file_mode = other.file_mode();
201 metadata.file_size = other.file_size();
202 metadata.file_time = other.file_time();
203 }
204
205 fn reset_metadata(&self) {
206 let mut metadata = self.file_metadata.borrow_mut();
207 let metadata = metadata.get_or_insert_with(|| self.get_metadata());
208 metadata.file_size = 0;
209 metadata.file_time = SystemTime::UNIX_EPOCH;
210 }
211
212 fn file_mode(&self) -> u32 {
213 let mut metadata = self.file_metadata.borrow_mut();
214 let metadata = metadata.get_or_insert_with(|| self.get_metadata());
215 metadata.file_mode
216 }
217
218 #[cfg(unix)]
219 fn owner_uid(&self) -> uid_t {
220 let mut metadata = self.file_metadata.borrow_mut();
221 let metadata = metadata.get_or_insert_with(|| self.get_metadata());
222 metadata.owner_uid
223 }
224
225 #[cfg(unix)]
226 fn owner_gid(&self) -> gid_t {
227 let mut metadata = self.file_metadata.borrow_mut();
228 let metadata = metadata.get_or_insert_with(|| self.get_metadata());
229 metadata.owner_gid
230 }
231
232 fn file_size(&self) -> u64 {
233 let mut metadata = self.file_metadata.borrow_mut();
234 let metadata = metadata.get_or_insert_with(|| self.get_metadata());
235 metadata.file_size
236 }
237
238 fn file_time(&self) -> SystemTime {
239 let mut metadata = self.file_metadata.borrow_mut();
240 let metadata = metadata.get_or_insert_with(|| self.get_metadata());
241 metadata.file_time
242 }
243}
244
245fn crc32_file(path: &Path) -> MyResult<u32> {
246 let mut file = File::open(path)?;
247 let mut hasher = Hasher::new();
248 let mut buffer = [0u8; 8192];
249 loop {
250 let count = file.read(&mut buffer)?;
251 if count == 0 {
252 break;
253 }
254 hasher.update(&buffer[..count]);
255 }
256 Ok(hasher.finalize())
257}