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 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}