libfct4/
file_parser.rs

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                // calculate chunk count and last chunk size
26                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        // with this check, t
55        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        // read file_path_len amount of bytes
63        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        //println!("{:?}", parser);
68
69        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}