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