ex_cli/fs/
file.rs

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