1use std::fs::{self};
2use std::path::PathBuf;
3use std::io::Read;
4use crate::fs_operations;
5
6#[derive(Default, Debug, Clone)]
7pub struct FileParser {
8 pub file_path: PathBuf,
9 pub chunk_count: u32,
10 pub last_chunk_size: u16
11}
12
13impl FileParser {
14 pub fn from_file(file_path: &PathBuf, root_dir: &PathBuf, chunk_size: u16) -> Result<Self, &'static str> {
15 match fs::metadata(&file_path){
16 Ok(file_info) => {
17 let mut parser = FileParser::default();
18 if file_info.permissions().readonly() {
19 return Err("File is readonly");
20 }
21 parser.file_path = match fs_operations::format_path(root_dir, file_path) {
22 Ok(path) => path,
23 Err(_) => return Err("Could not format path")
24 };
25 let chunk_count_result = u32::try_from(file_info.len() / chunk_size as u64);
27
28 let last_chunk_size_result = u16::try_from(file_info.len() % chunk_size as u64);
29
30 match chunk_count_result {
31 Ok(chunk_count) => parser.chunk_count = chunk_count,
32 Err(_) => return Err("File size is too big")
33 }
34
35 match last_chunk_size_result {
36 Ok(value) => parser.last_chunk_size = value,
37 Err(_) => return Err("Final Chunk is too big")
38 }
39 return Ok(parser);
40 }
41 Err(_) => return Err("File not found")
42 }
43 }
44
45 pub fn from_archive<R: Read>(file: &mut R) -> Result<Self, &'static str> {
46 const PROPERTY_FIELD_LEN: usize = 8;
47 let mut parser = FileParser::default();
48 let mut buffer = [0u8; PROPERTY_FIELD_LEN];
49
50 let bytes_read = file.read(&mut buffer).expect("Failed to read file header data");
51 if bytes_read == 0 {
52 return Err("File is empty or EOF reached");
53 }
54 if bytes_read != PROPERTY_FIELD_LEN {
56 return Err("File header is incomplete");
57 }
58 parser.chunk_count = u32::from_le_bytes(buffer[..4].try_into().unwrap());
59 parser.last_chunk_size = u16::from_le_bytes(buffer[4..6].try_into().unwrap());
60 let file_path_len = u16::from_le_bytes(buffer[6..8].try_into().unwrap()) as usize;
61
62 let mut file_path_buffer = vec![0u8; file_path_len];
64 file.read_exact(&mut file_path_buffer).expect("Failed to read file path");
65 parser.file_path = PathBuf::from(String::from_utf8(file_path_buffer).expect("Failed to convert file path to string"));
66
67 return Ok(parser);
70 }
71
72 pub fn generate_header(&self) -> Result<Vec<u8>, &'static str> {
73 let mut header = Vec::new();
74 header.extend_from_slice(&self.chunk_count.to_le_bytes());
75 header.extend_from_slice(&self.last_chunk_size.to_le_bytes());
76
77 let file_path_bytes = self.file_path.to_str().unwrap().as_bytes();
78 match u16::try_from(file_path_bytes.len()) {
79 Ok(value) => header.extend_from_slice(&value.to_le_bytes()),
80 Err(_) => return Err("File path is too big")
81 }
82 header.extend_from_slice(&file_path_bytes);
83 return Ok(header);
84 }
85
86 pub fn get_header_size(&self) -> usize {
87 return 8 + self.file_path.to_str().unwrap().as_bytes().len();
88 }
89}
90
91impl std::fmt::Display for FileParser {
92 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93 return write!(f, "File Name: \"{}\"; Chunk Count: {}; Last Chunk Size: {}", self.file_path.display(), self.chunk_count, self.last_chunk_size);
94 }
95}