1use {
9 crate::error::Error,
10 chrono::{DateTime, TimeZone, Utc},
11 simple_file_manifest::{
12 S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR,
13 },
14 std::ffi::CString,
15};
16
17#[derive(Clone, Copy, Debug)]
19pub enum BomPathType {
20 File,
22
23 Directory,
25
26 Link,
28
29 Dev,
31
32 Other(u8),
34}
35
36impl From<u8> for BomPathType {
37 fn from(v: u8) -> Self {
38 match v {
39 1 => Self::File,
40 2 => Self::Directory,
41 3 => Self::Link,
42 4 => Self::Dev,
43 _ => Self::Other(v),
44 }
45 }
46}
47
48impl From<BomPathType> for u8 {
49 fn from(t: BomPathType) -> Self {
50 match t {
51 BomPathType::File => 1,
52 BomPathType::Directory => 2,
53 BomPathType::Link => 3,
54 BomPathType::Dev => 4,
55 BomPathType::Other(v) => v,
56 }
57 }
58}
59
60#[derive(Clone, Debug)]
65pub struct BomPath {
66 pub(crate) path_type: BomPathType,
68
69 pub(crate) path: String,
71
72 pub(crate) file_mode: u16,
73 pub(crate) user_id: u32,
74 pub(crate) group_id: u32,
75 pub(crate) mtime: DateTime<Utc>,
76 pub(crate) size: usize,
77 pub(crate) crc32: Option<u32>,
78 pub(crate) link_name: Option<String>,
79}
80
81impl BomPath {
82 pub fn from_record(
84 path: String,
85 record: &crate::format::BomBlockPathRecord,
86 ) -> Result<Self, Error> {
87 let mtime = Utc
88 .timestamp_opt(record.mtime as _, 0)
89 .single()
90 .ok_or(Error::BadTime)?;
91
92 let path_type = BomPathType::from(record.path_type);
93
94 let crc32 = match path_type {
95 BomPathType::File | BomPathType::Link => Some(record.checksum_or_type),
96 BomPathType::Directory | BomPathType::Dev | BomPathType::Other(_) => None,
97 };
98
99 Ok(Self {
100 path_type,
101 path,
102 file_mode: record.mode,
103 user_id: record.user,
104 group_id: record.group,
105 mtime,
106 size: record.size as _,
107 crc32,
108 link_name: record.string_link_name(),
109 })
110 }
111
112 pub fn path_type(&self) -> BomPathType {
114 self.path_type
115 }
116
117 pub fn path(&self) -> &str {
119 &self.path
120 }
121
122 pub fn file_mode(&self) -> u16 {
124 self.file_mode
125 }
126
127 pub fn set_file_mode(&mut self, mode: u16) -> u16 {
129 let old = self.file_mode;
130 self.file_mode = mode;
131 old
132 }
133
134 pub fn symbolic_mode(&self) -> String {
138 let mut mode = String::with_capacity(10);
139
140 mode.push(match self.path_type {
141 BomPathType::Directory => 'd',
142 BomPathType::File => '-',
143 BomPathType::Link => 'l',
144 BomPathType::Dev => '?',
145 BomPathType::Other(_) => '?',
146 });
147
148 mode.push(if self.file_mode as u32 & S_IRUSR != 0 {
149 'r'
150 } else {
151 '-'
152 });
153 mode.push(if self.file_mode as u32 & S_IWUSR != 0 {
154 'w'
155 } else {
156 '-'
157 });
158 mode.push(if self.file_mode as u32 & S_IXUSR != 0 {
159 'x'
160 } else {
161 '-'
162 });
163 mode.push(if self.file_mode as u32 & S_IRGRP != 0 {
164 'r'
165 } else {
166 '-'
167 });
168 mode.push(if self.file_mode as u32 & S_IWGRP != 0 {
169 'w'
170 } else {
171 '-'
172 });
173 mode.push(if self.file_mode as u32 & S_IXGRP != 0 {
174 'x'
175 } else {
176 '-'
177 });
178 mode.push(if self.file_mode as u32 & S_IROTH != 0 {
179 'r'
180 } else {
181 '-'
182 });
183 mode.push(if self.file_mode as u32 & S_IWOTH != 0 {
184 'w'
185 } else {
186 '-'
187 });
188 mode.push(if self.file_mode as u32 & S_IXOTH != 0 {
189 'x'
190 } else {
191 '-'
192 });
193
194 mode
195 }
196
197 pub fn user_id(&self) -> u32 {
199 self.user_id
200 }
201
202 pub fn set_user_id(&mut self, uid: u32) -> u32 {
204 let old = self.user_id;
205 self.user_id = uid;
206 old
207 }
208
209 pub fn group_id(&self) -> u32 {
211 self.group_id
212 }
213
214 pub fn set_group_id(&mut self, gid: u32) -> u32 {
216 let old = self.user_id;
217 self.group_id = gid;
218 old
219 }
220
221 pub fn modified_time(&self) -> &DateTime<Utc> {
223 &self.mtime
224 }
225
226 pub fn set_modified_time(&mut self, mtime: DateTime<Utc>) -> DateTime<Utc> {
228 let old = self.mtime;
229 self.mtime = mtime;
230 old
231 }
232
233 pub fn size(&self) -> usize {
235 self.size
236 }
237
238 pub fn set_size(&mut self, size: usize) -> usize {
240 let old = self.size;
241 self.size = size;
242 old
243 }
244
245 pub fn crc32(&self) -> Option<u32> {
249 self.crc32
250 }
251
252 pub fn set_crc32(&mut self, value: Option<u32>) -> Option<u32> {
254 let old = self.crc32;
255 self.crc32 = value;
256 old
257 }
258
259 pub fn link_name(&self) -> Option<&str> {
261 self.link_name.as_deref()
262 }
263
264 pub fn link_name_cstring(&self) -> Option<CString> {
266 if let Some(link_name) = &self.link_name {
267 let mut data = Vec::<u8>::with_capacity(link_name.as_bytes().len() + 1);
268 data.extend(link_name.as_bytes());
269 data.push(0);
270
271 Some(CString::new(data).expect("should be valid C string"))
272 } else {
273 None
274 }
275 }
276
277 pub fn set_link_name(&mut self, value: Option<String>) -> Option<String> {
279 let old = self.link_name.clone();
280 self.link_name = value;
281 old
282 }
283}