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