Skip to main content

ex_cli/fs/
entry.rs

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}