lusl/serialize/
meta.rs

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        // path clearance
208        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        // 131 means it is a file, and the size takes 3 bytes.
336        // And size bytes are little endian.
337        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        // Restore file path
373        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        // Restore file type
378        let flag_and_byte_count = binary.pop_front().unwrap();
379        meta2.deserialize_type(flag_and_byte_count);
380
381        // Restore file size
382        let size_count = (flag_and_byte_count & 0xF) as usize;
383        meta2.deserialize_size(&binary.drain(..size_count).collect::<Vec<u8>>());
384
385        // Restore checksum
386        meta2.deserialize_checksum(&binary.drain(..16).collect::<Vec<u8>>());
387
388        assert_eq!(meta1, meta2);
389    }
390}