ex_cli/fs/
file.rs

1use crate::config::FileKind;
2use chrono::{DateTime, Utc};
3use std::cmp::Ordering;
4use std::hash::{Hash, Hasher};
5use std::path::{Path, PathBuf};
6use std::rc::Rc;
7use std::time::UNIX_EPOCH;
8
9pub type Signature = [u8; 4];
10
11pub struct File {
12    pub abs_dir: PathBuf,
13    pub rel_dir: PathBuf,
14    pub file_depth: usize,
15    pub zip_depth: Option<usize>,
16    pub file_name: String,
17    pub file_ext: String,
18    pub file_type: FileKind,
19    pub file_mode: u32,
20    pub owner_user: Option<Rc<String>>,
21    pub owner_group: Option<Rc<String>>,
22    pub file_size: u64,
23    pub file_time: DateTime<Utc>,
24    pub file_sig: Signature,
25    #[cfg(windows)]
26    pub file_ver: Option<String>,
27    pub link_data: Option<(PathBuf, FileKind)>,
28}
29
30impl File {
31    pub fn new(
32        abs_dir: PathBuf,
33        rel_dir: PathBuf,
34        file_depth: usize,
35        zip_depth: Option<usize>,
36        file_name: String,
37        file_ext: String,
38        file_type: FileKind,
39    ) -> Self {
40        Self {
41            abs_dir,
42            rel_dir,
43            file_depth,
44            zip_depth,
45            file_name,
46            file_ext,
47            file_type,
48            file_mode: 0,
49            owner_user: None,
50            owner_group: None,
51            file_size: 0,
52            file_time: DateTime::<Utc>::from(UNIX_EPOCH),
53            file_sig: [0; 4],
54            #[cfg(windows)]
55            file_ver: None,
56            link_data: None,
57        }
58    }
59
60    #[cfg(test)]
61    pub fn with_dirs(mut self, abs_dir: PathBuf, rel_dir: PathBuf) -> Self {
62        self.abs_dir = abs_dir;
63        self.rel_dir = rel_dir;
64        self
65    }
66
67    #[cfg(test)]
68    pub fn with_zip_depth(mut self, zip_depth: Option<usize>) -> Self {
69        self.zip_depth = zip_depth;
70        self
71    }
72
73    #[cfg(test)]
74    pub fn with_type(mut self, file_type: FileKind) -> Self {
75        self.file_type = file_type;
76        self
77    }
78
79    pub fn with_mode(mut self, file_mode: u32) -> Self {
80        self.file_mode = file_mode;
81        self
82    }
83
84    pub fn with_owner(
85        mut self,
86        owner_user: Option<Rc<String>>,
87        owner_group: Option<Rc<String>>,
88    ) -> Self {
89        self.owner_user = owner_user;
90        self.owner_group = owner_group;
91        self
92    }
93
94    #[cfg(test)]
95    pub fn with_owner_ref(
96        mut self,
97        owner_user: &str,
98        owner_group: &str,
99    ) -> Self {
100        self.owner_user = Some(Rc::new(String::from(owner_user)));
101        self.owner_group = Some(Rc::new(String::from(owner_group)));
102        self
103    }
104
105    pub fn with_size(mut self, file_size: u64) -> Self {
106        self.file_size = file_size;
107        self
108    }
109
110    pub fn with_time(mut self, file_time: DateTime<Utc>) -> Self {
111        self.file_time = file_time;
112        self
113    }
114
115    #[cfg(test)]
116    pub fn with_date(mut self, year: i32, month: u32, day: u32) -> Self {
117        use chrono::TimeZone;
118        self.file_time = Utc.with_ymd_and_hms(year, month, day, 0, 0, 0).unwrap();
119        self
120    }
121
122    pub fn with_sig(mut self, file_sig: Option<Signature>) -> Self {
123        if let Some(file_sig) = file_sig {
124            self.file_sig = file_sig;
125        }
126        self
127    }
128
129    #[cfg(test)]
130    pub fn with_sig_str(mut self, file_sig: &str) -> Self {
131        use std::cmp;
132        let file_sig = file_sig.as_bytes();
133        let len = cmp::min(file_sig.len(), 4);
134        self.file_sig[0..len].copy_from_slice(file_sig);
135        self
136    }
137
138    #[cfg(windows)]
139    pub fn with_version(mut self, file_ver: String) -> Self {
140        self.file_ver = Some(file_ver);
141        self
142    }
143
144    pub fn with_link(mut self, link_path: PathBuf, link_type: FileKind) -> Self {
145        self.link_data = Some((link_path, link_type));
146        self
147    }
148
149    pub fn get_path(&self) -> PathBuf {
150        self.abs_dir.join(&self.file_name)
151    }
152
153    pub fn select_dir(&self, abs_path: bool) -> &Path {
154        if abs_path {
155            &self.abs_dir
156        } else {
157            &self.rel_dir
158        }
159    }
160
161    pub fn select_parent_for_indent(&self) -> Option<PathBuf> {
162        if self.file_type == FileKind::Dir {
163            self.rel_dir.parent().map(PathBuf::from)
164        } else {
165            Some(self.rel_dir.clone())
166        }
167    }
168
169    pub fn group_dir_before_file(&self) -> u8 {
170        if self.file_type == FileKind::Dir { 0 } else { 1 }
171    }
172}
173
174impl PartialEq for File {
175    fn eq(&self, other: &Self) -> bool {
176        (self.abs_dir == other.abs_dir) && (self.file_name == other.file_name)
177    }
178}
179
180impl Eq for File {
181}
182
183impl PartialOrd for File {
184    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
185        Some(Ord::cmp(self, other))
186    }
187}
188
189impl Ord for File {
190    fn cmp(&self, other: &Self) -> Ordering {
191        match PathBuf::cmp(&self.abs_dir, &other.abs_dir) {
192            Ordering::Less => Ordering::Less,
193            Ordering::Greater => Ordering::Greater,
194            Ordering::Equal => String::cmp(&self.file_name, &other.file_name),
195        }
196    }
197}
198
199impl Hash for File {
200    fn hash<H: Hasher>(&self, state: &mut H) {
201        self.abs_dir.hash(state);
202        self.file_name.hash(state);
203    }
204}