slpm_file/
header_binary_v0.rs

1use std::convert::TryFrom;
2
3use pad::PadStr;
4
5use crate::datatype::DataType;
6use crate::header_v0::HeaderV0;
7
8//Do not change!!!
9const MAGIC_NUMBER_SIZE: usize = 10;
10const MAGIC_NUMBER: [u8; MAGIC_NUMBER_SIZE] = *b"slpm-filef";
11const VERSION: u16 = 0;
12
13const HEADER_SIZE: usize = 1024;
14
15const VERSION_SIZE: usize = 2;
16const DATATYPE_SIZE: usize = 1;
17const NAME_SIZE: usize = 128;
18const CREATED_SIZE: usize = 8;
19const EDITED_SIZE: usize = 8;
20const FILE_NAME_SIZE: usize = 128;
21const BUFFER_SIZE_SIZE: usize = 8;
22
23/// All values are big endian
24#[derive(Clone, Hash, Debug, Eq, Ord, PartialOrd, PartialEq)]
25pub struct HeaderBinaryV0 {
26	/// u32 Magic number for exact identification of file-format does not exist on processed header
27	pub magic_number: [u8; MAGIC_NUMBER_SIZE],
28
29	/// u16 Version indicating which struct to deserialize to (for future)
30	pub version: [u8; VERSION_SIZE],
31
32	/// u8 Matched to enum DataType
33	pub datatype: [u8; DATATYPE_SIZE],
34
35	/// UTF-8 string with 1024 bits capacity
36	pub name: [u8; NAME_SIZE],
37
38	/// u64 Create date in seconds after epoch
39	pub created: [u8; CREATED_SIZE],
40
41	/// u64 Edit date in seconds after epoch
42	pub edited: [u8; EDITED_SIZE],
43
44	/// UTF-8 string with 1024 bits capacity
45	pub file_name: [u8; FILE_NAME_SIZE],
46
47	/// u64 when required, store buffer size for decoding purposes (maybe have buffer size = cypher length when not used?)
48	pub buffer_size: [u8; BUFFER_SIZE_SIZE],
49
50}
51
52impl HeaderBinaryV0 {
53	#[must_use]
54	pub fn to_bytes(&self) -> Vec<u8> {
55
56		// Ordering is highly important and needs to match the from_bytes function
57		let mut output = Vec::new();
58		output.extend_from_slice(&self.magic_number);
59		output.extend_from_slice(&self.version);
60		output.extend_from_slice(&self.datatype);
61		output.extend_from_slice(&self.name);
62		output.extend_from_slice(&self.created);
63		output.extend_from_slice(&self.edited);
64		output.extend_from_slice(&self.file_name);
65		output.extend_from_slice(&self.buffer_size);
66		output.resize(HEADER_SIZE, 0);
67		output
68	}
69
70	/// # Panics
71	///
72	/// Should never panic
73	#[must_use]
74	pub fn from_bytes(bytes: &[u8; HEADER_SIZE]) -> Self {
75		let mut position = 0_usize;
76
77		let magic_number = <[u8; MAGIC_NUMBER_SIZE]>::try_from(&bytes[position..position + MAGIC_NUMBER_SIZE]).unwrap();
78		position += MAGIC_NUMBER_SIZE;
79
80		let version = <[u8; VERSION_SIZE]>::try_from(&bytes[position..position + VERSION_SIZE]).unwrap();
81		position += VERSION_SIZE;
82
83		#[allow(clippy::range_plus_one)] // DATATYPE_SIZE equals 1 where Clippy wants to use an inclusive range
84		let datatype = <[u8; DATATYPE_SIZE]>::try_from(&bytes[position..position + DATATYPE_SIZE]).unwrap();
85		position += DATATYPE_SIZE;
86
87		let name = <[u8; NAME_SIZE]>::try_from(&bytes[position..position + NAME_SIZE]).unwrap();
88		position += NAME_SIZE;
89
90		let created = <[u8; CREATED_SIZE]>::try_from(&bytes[position..position + CREATED_SIZE]).unwrap();
91		position += CREATED_SIZE;
92
93		let edited = <[u8; EDITED_SIZE]>::try_from(&bytes[position..position + EDITED_SIZE]).unwrap();
94		position += EDITED_SIZE;
95
96		let file_name = <[u8; FILE_NAME_SIZE]>::try_from(&bytes[position..position + FILE_NAME_SIZE]).unwrap();
97		position += FILE_NAME_SIZE;
98
99		let buffer_size = <[u8; BUFFER_SIZE_SIZE]>::try_from(&bytes[position..position + BUFFER_SIZE_SIZE]).unwrap();
100		// position += BUFFER_SIZE_SIZE; Uncomment when adding further entries after this
101
102		Self {
103			magic_number,
104			version,
105			datatype,
106			name,
107			created,
108			edited,
109			file_name,
110			buffer_size,
111		}
112	}
113	/// # Panics
114	///
115	///Panics when any of the values are too long (see documentation on binary header limits)
116	#[must_use]
117	pub fn from_parameters(datatype: &DataType, name: &str, old_create_date: Option<i64>, file_name: &str, buffer_size: u64) -> Self {
118		let mut create_date = chrono::Local::now().timestamp();
119		if let Some(create) = old_create_date {
120			create_date = create;
121		}
122
123		let datatype_id: u8;
124		match datatype {
125			DataType::Password => { datatype_id = 0 }
126			DataType::File => { datatype_id = 1 }
127		}
128
129		let name_padded = name.pad_to_width(NAME_SIZE);
130		let file_name_padded = file_name.pad_to_width(FILE_NAME_SIZE);
131
132		Self {
133			magic_number: MAGIC_NUMBER,
134			version: VERSION.to_be_bytes(), //Increment for new file
135			datatype: datatype_id.to_be_bytes(),
136			name: <[u8; NAME_SIZE]>::try_from(name_padded.as_bytes()).unwrap(),
137			created: create_date.to_be_bytes(),
138			edited: chrono::Local::now().timestamp().to_be_bytes(),
139			file_name: <[u8; NAME_SIZE]>::try_from(file_name_padded.as_bytes()).unwrap(),
140			buffer_size: buffer_size.to_be_bytes(),
141		}
142	}
143	/// # Panics
144	///
145	/// Panics when any of the values are not the correct length
146	#[must_use]
147	pub fn from_header(header: &HeaderV0) -> Self {
148		let data_type: u8;
149		match header.datatype {
150			DataType::Password => data_type = 0,
151			DataType::File => data_type = 1
152		}
153		Self {
154			magic_number: MAGIC_NUMBER,
155			version: header.version.to_be_bytes(), //Increment for new file
156			datatype: data_type.to_be_bytes(),
157			name: <[u8; NAME_SIZE]>::try_from(header.name.as_bytes()).unwrap(),
158			created: header.created.to_be_bytes(),
159			edited: header.edited.to_be_bytes(),
160			file_name: <[u8; FILE_NAME_SIZE]>::try_from(header.file_name.as_bytes()).unwrap(),
161			buffer_size: header.buffer_size.to_be_bytes(),
162		}
163	}
164
165	/// # Panics
166	///
167	/// Panics when any of the header values are not parsable to the given types
168	#[must_use]pub fn to_header(&self) -> HeaderV0 {
169		let datatype: DataType;
170		match u8::from_be_bytes(self.datatype) {
171			0 => datatype = DataType::Password,
172			1 => datatype = DataType::File,
173			_ => {panic!("cannot match datatype")}
174		}
175
176		HeaderV0 {
177			version: VERSION,
178			datatype,
179			name: String::from_utf8(Vec::from(self.name)).unwrap(),
180			created: u64::from_be_bytes(self.created),
181			edited: u64::from_be_bytes(self.created),
182			file_name: String::from_utf8(Vec::from(self.file_name)).unwrap(),
183			buffer_size: u64::from_be_bytes(self.buffer_size)
184		}
185	}
186}