1use std::fs::File;
2use std::path::{Path, PathBuf};
3
4use crate::binary::{binary_to_u64, get_checksum, is_flag_true};
5
6const FILE_FLAG: u8 = 0x80;
7const DIR_FLAG: u8 = 0x40;
8const SYMLINK_FLAG: u8 = 0x20;
9
10#[derive(Debug)]
11pub struct MetaData {
12 path: PathBuf,
13 size: u64,
14 is_file: bool,
15 is_dir: bool,
16 is_symlink: bool,
17 checksum: Option<Vec<u8>>,
18}
19
20impl MetaData {
21 pub fn new() -> MetaData {
22 MetaData {
23 path: PathBuf::new(),
24 size: 0,
25 is_file: false,
26 is_dir: false,
27 is_symlink: false,
28 checksum: None,
29 }
30 }
31
32 pub fn path(&self) -> &PathBuf {
33 &self.path
34 }
35
36 pub fn size(&self) -> u64 {
37 self.size
38 }
39
40 pub fn checksum(&self) -> &Option<Vec<u8>> {
41 &self.checksum
42 }
43
44 pub fn strip_prefix<T: AsRef<Path>>(&mut self, root: T) {
45 self.path = self.path.strip_prefix(root).unwrap().to_path_buf()
46 }
47
48 fn serialize_path(&self) -> Vec<u8> {
49 let mut binary: Vec<u8> = Vec::new();
50 let mut name = self.path.to_str().unwrap().to_string();
51 while name.len() > u16::MAX.into() {
52 name.pop();
53 }
54 let length: u16 = name.len().try_into().unwrap();
55 let length = length.to_be_bytes();
56 binary.push(length[0]);
57 binary.push(length[1]);
58
59 for i in name.bytes() {
60 binary.push(i);
61 }
62
63 binary
64 }
65
66 fn serialize_type_size(&self) -> Vec<u8> {
67 let mut binary: Vec<u8> = Vec::new();
68
69 let mut flag_and_size: u8 = 0x0;
70 if let true = self.is_file {
71 flag_and_size += FILE_FLAG;
72 }
73 if let true = self.is_dir {
74 flag_and_size += DIR_FLAG;
75 }
76 if let true = self.is_symlink {
77 flag_and_size += SYMLINK_FLAG;
78 }
79
80 let mut index = 0;
81 for byte in self.size.to_be_bytes() {
82 if byte == 0 {
83 index += 1;
84 } else {
85 break;
86 }
87 }
88 let size_bytes_count = (self.size.to_le_bytes().len() - index) as u8;
89 flag_and_size += size_bytes_count;
90 binary.push(flag_and_size);
91 for i in &self.size.to_le_bytes()[..size_bytes_count as usize] {
92 binary.push(*i);
93 }
94
95 binary
96 }
97
98 fn serialize_checksum(&self) -> Vec<u8> {
99 let mut binary: Vec<u8> = Vec::new();
100 match &self.checksum {
101 Some(c) => {
102 for i in c {
103 binary.push(*i);
104 }
105 }
106 None => {
107 for _ in 0..16 {
108 binary.push(0);
109 }
110 }
111 }
112 binary
113 }
114
115 pub fn serialize(&self) -> Vec<u8> {
116 let mut binary: Vec<u8> = Vec::new();
117 binary.append(&mut self.serialize_path());
118 binary.append(&mut self.serialize_type_size());
119 binary.append(&mut self.serialize_checksum());
120 binary
121 }
122
123 pub fn deserialize_path(&mut self, name_binary: &[u8]) {
124 self.path = match String::from_utf8(name_binary.to_vec()) {
125 Ok(n) => PathBuf::from(n),
126 Err(_) => PathBuf::from("untitled.bin"),
127 };
128 }
129 pub fn deserialize_type(&mut self, type_flag: u8) {
130 self.is_file = is_flag_true(type_flag, FILE_FLAG);
131 self.is_dir = is_flag_true(type_flag, DIR_FLAG);
132 self.is_symlink = is_flag_true(type_flag, SYMLINK_FLAG);
133 }
134
135 pub fn deserialize_size(&mut self, size_binary: &[u8]) {
136 self.size = binary_to_u64(size_binary);
137 }
138
139 pub fn deserialize_checksum(&mut self, checksum_binary: &[u8]) {
140 self.checksum = Some(checksum_binary.to_vec());
141 }
142}
143
144impl<T: AsRef<Path>> From<&T> for MetaData {
145 fn from(file_path: &T) -> Self {
146 match File::open(&file_path) {
147 Ok(file) => {
148 return MetaData {
149 path: file_path.as_ref().to_path_buf(),
150 size: match file.metadata() {
151 Ok(m) => m.len(),
152 Err(_) => 0,
153 },
154 is_file: match file.metadata() {
155 Ok(m) => m.is_file(),
156 Err(_) => false,
157 },
158 is_dir: match file.metadata() {
159 Ok(m) => m.is_dir(),
160 Err(_) => false,
161 },
162 is_symlink: match file.metadata() {
163 Ok(m) => m.is_symlink(),
164 Err(_) => false,
165 },
166 checksum: { Some(get_checksum(file)) },
167 }
168 }
169 Err(_) => return MetaData::new(),
170 };
171 }
172}
173
174impl PartialEq for MetaData {
175 fn eq(&self, other: &Self) -> bool {
176 self.path == other.path
177 && self.size == other.size
178 && self.is_file == other.is_file
179 && self.is_dir == other.is_dir
180 && self.is_symlink == other.is_symlink
181 && self.checksum == other.checksum
182 }
183}
184
185#[cfg(test)]
186mod tests {
187
188 use std::{collections::VecDeque, path::PathBuf};
189 use hex::decode;
190
191 use crate::serialize::get_file_list;
192
193 use super::MetaData;
194
195 const ORIGINAL_FILE: &str = "tests/original_images/dir1/board-g43968feec_1920.jpg";
196
197 #[test]
198 fn metadata_compare_test() {
199 let original = PathBuf::from("tests");
200
201 let original_file_vec = get_file_list(&original).unwrap();
202 let mut original_metadata_vec = Vec::new();
203 for f in original_file_vec {
204 let meta = MetaData::from(&f);
205 original_metadata_vec.push(meta);
206 }
207 let mut original_metadata_vec: Vec<MetaData> = original_metadata_vec
209 .iter()
210 .map(|m| MetaData {
211 path: PathBuf::from(m.path.file_name().unwrap()),
212 size: m.size,
213 is_file: m.is_file,
214 is_dir: m.is_dir,
215 is_symlink: m.is_symlink,
216 checksum: Some(m.checksum.clone().unwrap()),
217 })
218 .collect();
219 let mut result_metadata_vec = Vec::from([
220 MetaData {
221 path: PathBuf::from("colorful-2174045.png"),
222 size: 464447,
223 is_file: true,
224 is_dir: false,
225 is_symlink: false,
226 checksum: Some(decode("4e42993bfd2756df48b646d68433db1e").unwrap()),
227 },
228 MetaData {
229 path: PathBuf::from("capsules-g869437822_1920.jpg"),
230 size: 371728,
231 is_file: true,
232 is_dir: false,
233 is_symlink: false,
234 checksum: Some(decode("60e191a914756ff7ae259e33f40f20da").unwrap()),
235 },
236 MetaData {
237 path: PathBuf::from("board-g43968feec_1920.jpg"),
238 size: 914433,
239 is_file: true,
240 is_dir: false,
241 is_symlink: false,
242 checksum: Some(decode("37ca14866812327e1776d8cbb250501c").unwrap()),
243 },
244 MetaData {
245 path: PathBuf::from("laboratory-g8f9267f5f_1920.jpg"),
246 size: 6737,
247 is_file: true,
248 is_dir: false,
249 is_symlink: false,
250 checksum: Some(decode("0c37be929cdc29b5ac0914104cda75aa").unwrap()),
251 },
252 MetaData {
253 path: PathBuf::from("폭발.jpg"),
254 size: 562560,
255 is_file: true,
256 is_dir: false,
257 is_symlink: false,
258 checksum: Some(decode("4753aff9b06a34832ad1de0a69d5dcd3").unwrap()),
259 },
260 MetaData {
261 path: PathBuf::from("digitization-1755812_1920.jpg"),
262 size: 468460,
263 is_file: true,
264 is_dir: false,
265 is_symlink: false,
266 checksum: Some(decode("4b6cab47e9193a4aebe4c8c6b7c88c1b").unwrap()),
267 },
268 MetaData {
269 path: PathBuf::from("syringe-ge5e95bfe6_1920.jpg"),
270 size: 253304,
271 is_file: true,
272 is_dir: false,
273 is_symlink: false,
274 checksum: Some(decode("a7385d8a719c3036a857e21225c5bd6b").unwrap()),
275 },
276 MetaData {
277 path: PathBuf::from("books-g6617d4d97_1920.jpg"),
278 size: 564004,
279 is_file: true,
280 is_dir: false,
281 is_symlink: false,
282 checksum: Some(decode("65aee1442129f56a0a6157c6b55f80c9").unwrap()),
283 },
284 MetaData {
285 path: PathBuf::from("test-pattern-152459.png"),
286 size: 55262,
287 is_file: true,
288 is_dir: false,
289 is_symlink: false,
290 checksum: Some(decode("a09d4eab0326ba5403369035531f9308").unwrap()),
291 },
292 MetaData {
293 path: PathBuf::from("tv-g87676cdfb_1280.png"),
294 size: 1280855,
295 is_file: true,
296 is_dir: false,
297 is_symlink: false,
298 checksum: Some(decode("91517821bc6851b0d9abec5d5adea961").unwrap()),
299 },
300 ]);
301 original_metadata_vec.sort_by_key(|m| m.path.clone());
302 result_metadata_vec.sort_by_key(|m| m.path.clone());
303 assert_eq!(original_metadata_vec, result_metadata_vec);
304 }
305
306 #[test]
307 fn name_serialize_test() {
308 let meta = MetaData::from(&PathBuf::from(ORIGINAL_FILE));
309 assert_eq!(meta.serialize()[0], 0);
310 assert_eq!(meta.serialize()[1], 52);
311
312 let expected_meta1_bi: [u8; 52] = [
313 116, 101, 115, 116, 115, 47, 111, 114, 105, 103, 105, 110, 97, 108, 95, 105, 109, 97,
314 103, 101, 115, 47, 100, 105, 114, 49, 47, 98, 111, 97, 114, 100, 45, 103, 52, 51, 57,
315 54, 56, 102, 101, 101, 99, 95, 49, 57, 50, 48, 46, 106, 112, 103,
316 ];
317 let meta1_binary = meta.serialize();
318 let type_size_index = meta1_binary[0] as usize * 0x100 + meta1_binary[1] as usize;
319 assert_eq!(&meta.serialize()[2..type_size_index + 2], expected_meta1_bi);
320 }
321
322 #[test]
323 fn flag_size_serialize_test() {
324 let meta1 = MetaData::from(&PathBuf::from(ORIGINAL_FILE));
325 let binary = meta1.serialize();
326 let name_end_index = binary[0] as usize * 0x100 + binary[1] as usize;
327 let type_size = binary[name_end_index + 2];
328
329 assert_eq!(type_size & 0x80, 0x80);
330 assert_eq!(type_size & 0x40, 0);
331 assert_eq!(type_size & 0x20, 0);
332
333 let type_size_index = (type_size & 0xF) as usize;
334 assert_eq!(type_size_index, 3);
335 assert_eq!(
338 &binary[name_end_index + 3..name_end_index + type_size_index + 3],
339 [1, 244, 13]
340 );
341 }
342
343 #[test]
344 fn checksum_serialize_test() {
345 let meta1 = MetaData::from(&PathBuf::from(ORIGINAL_FILE));
346
347 let binary = meta1.serialize();
348 let name_end_index = binary[0] as usize * 0x100 + binary[1] as usize;
349 let type_size = binary[name_end_index + 2];
350 let type_size_index = (type_size & 0xF) as usize;
351
352 let expected_checksum: [u8; 16] = [
353 55, 202, 20, 134, 104, 18, 50, 126, 23, 118, 216, 203, 178, 80, 80, 28
354 ];
355
356 assert_eq!(
357 &binary
358 [name_end_index + type_size_index + 3..name_end_index + type_size_index + 3 + 16],
359 expected_checksum
360 );
361 }
362
363 #[test]
364 fn metadata_serialize_test() {
365 let meta1 = MetaData::from(&PathBuf::from(ORIGINAL_FILE));
366 let mut binary = VecDeque::from_iter(meta1.serialize());
367
368 println!("{:?}", meta1);
369
370 let mut meta2 = MetaData::new();
371
372 let path_size = binary[0] as usize * 0x100 + binary[1] as usize;
374 binary.drain(..2);
375 meta2.deserialize_path(&binary.drain(..path_size).collect::<Vec<u8>>());
376
377 let flag_and_byte_count = binary.pop_front().unwrap();
379 meta2.deserialize_type(flag_and_byte_count);
380
381 let size_count = (flag_and_byte_count & 0xF) as usize;
383 meta2.deserialize_size(&binary.drain(..size_count).collect::<Vec<u8>>());
384
385 meta2.deserialize_checksum(&binary.drain(..16).collect::<Vec<u8>>());
387
388 assert_eq!(meta1, meta2);
389 }
390}