1use std::error::Error;
2use std::io::{Cursor, Read, Write};
3use std::time::{SystemTime, UNIX_EPOCH};
4use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
5
6#[derive(Debug)]
7pub struct Container {
8 pub comment: String,
9 pub x: u64,
10 pub y: u64,
11 pub z: u64,
12 pub files: Vec<File>
13}
14
15#[derive(Clone, Debug)]
16pub struct File {
17 pub name: String,
18 pub content: Vec<u8>
19}
20
21pub const Y_DIFFERENCE: u64 = 43;
22pub const Z_DIFFERENCE: u64 = 34;
23pub const MAGIC_NUMBER: u8 = 0x46;
24
25fn read_string_until_0x00(cursor: &mut Cursor<&[u8]>) -> Result<String, Box<dyn Error>> {
26 let mut buffer: Vec<u8> = Vec::new();
27
28 loop {
29 let mut byte = [0; 1];
30 cursor.read_exact(&mut byte)?;
31 if byte[0] == 0x00 {
32 break;
33 }
34
35 buffer.push(byte[0])
36 }
37
38 let string = String::from_utf8_lossy(&buffer).into_owned();
39 Ok(string)
40}
41
42impl Container {
43 pub fn new(comment: &str) -> Result<Container, Box<dyn Error>> {
44 let x = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
45
46 return Ok(Container {
47 comment: comment.to_string(),
48 x,
49 y: x + Y_DIFFERENCE,
50 z: x + Z_DIFFERENCE,
51 files: vec![]
52 })
53 }
54
55 pub fn from_bytes(bytes: &[u8]) -> Result<Container, Box<dyn Error>> {
56 let mut cursor = Cursor::new(bytes);
57
58 if cursor.read_u8()? != MAGIC_NUMBER {
59 return Err(Box::from("invalid or incorrect magic number"));
60 }
61
62 let comment = read_string_until_0x00(&mut cursor)?;
63 let x = cursor.read_u64::<LittleEndian>()?;
64 let y = x + Y_DIFFERENCE;
65 let z = x + Z_DIFFERENCE;
66 let file_count = cursor.read_u16::<LittleEndian>()?;
67
68 let mut files: Vec<File> = Vec::new();
69
70 for _ in 1..=file_count {
71 let name = read_string_until_0x00(&mut cursor)?;
72 let length = cursor.read_u64::<LittleEndian>()?;
73 let mut content = vec![0; length as usize];
74 cursor.read_exact(&mut content)?;
75 files.push(File {
76 name,
77 content
78 })
79 }
80
81
82 Ok(Container {x, y, z, comment, files})
83 }
84
85 pub fn add_file(&mut self, file: File) {
86 self.files.push(file)
87 }
88
89 pub fn remove_file(&mut self, name: String) {
90 self.files = self.files.iter().cloned().filter(|f| f.name != name).collect()
91 }
92
93 pub fn get_file(&self, name: String) -> Option<&File> {
94 self.files.iter().find(|f| f.name == name)
95 }
96
97 pub fn to_bytes(&self) -> Result<Vec<u8>, Box<dyn Error>> {
98 let mut bytes: Vec<u8> = Vec::new();
99 bytes.push(MAGIC_NUMBER);
100 bytes.write(self.comment.as_bytes())?;
101 bytes.push(0x00);
102 bytes.write_u64::<LittleEndian>(self.x)?;
103 bytes.write_u16::<LittleEndian>(self.files.len() as u16)?;
104
105 for f in self.files.iter() {
106 bytes.write(f.name.as_bytes())?;
107 bytes.push(0x00);
108 bytes.write_u64::<LittleEndian>(f.content.len() as u64)?;
109 bytes.write_all(f.content.as_slice())?;
110 }
111
112 Ok(bytes)
113 }
114}
115
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 #[test]
122 fn create_container_has_correct_values() {
123 let mut container = Container::new("Example").unwrap();
124 let current_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
125 assert_eq!(container.x, current_time);
126 assert_eq!(container.y, current_time + Y_DIFFERENCE);
127 assert_eq!(container.z, current_time + Z_DIFFERENCE);
128 assert_eq!(container.files.len(), 0); let file_name = "C:\\farting.png".to_string();
132 let file = File {
133 name: file_name.clone(),
134 content: vec![0x00, 0xF2]
135 };
136 container.add_file(file);
137 assert_eq!(container.files.len(), 1);
138
139 container.remove_file(file_name);
140 assert_eq!(container.files.len(), 0);
141 }
142
143 #[test]
144 fn read_write() {
145 let mut container = Container::new("The Best In The World").unwrap();
146
147 let file_name = "C:\\hello.png".to_string();
148 let file_content: [u8; 4] = [0x66, 0x66, 0x66, 0x66];
149 let file = File {name: file_name, content: file_content.to_vec()};
150 container.add_file(file);
151 let file2 = File {name: "better file name!!!!".to_string(), content: [0x23, 0x54, 0xFF].to_vec()};
152 container.add_file(file2);
153
154 let as_bytes = container.to_bytes().unwrap();
155
156 let new_container = Container::from_bytes(as_bytes.as_slice()).unwrap();
157
158 println!("{:?}", new_container.files);
159 }
160}