cat_dev/mion/proto/parameter/
mod.rs

1//! Protocols for speaking with the "parameter" port of the MION.
2//!
3//! The parameter port as of today is only known for getting/setting 512 bytes
4//! of parameters. These values are usually things reachable from other parts
5//! of the MION interface, but are available through the MION too.
6
7mod errors;
8pub mod well_known;
9
10pub use errors::*;
11
12use crate::{
13	errors::NetworkParseError,
14	mion::proto::parameter::well_known::{index_from_parameter_name, ValuableParameterDump},
15};
16use bytes::{BufMut, Bytes, BytesMut};
17use std::fmt::{Display, Formatter, Result as FmtResult};
18use valuable::{Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value};
19
20/// The type of MION Parameters Packet this is.
21#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Valuable)]
22pub enum PacketType {
23	/// This packet is a read request/response.
24	Read,
25	/// This packet is a write request/response.
26	Write,
27}
28
29impl From<&PacketType> for i32 {
30	fn from(value: &PacketType) -> Self {
31		match *value {
32			PacketType::Read => 0,
33			PacketType::Write => 1,
34		}
35	}
36}
37impl From<PacketType> for i32 {
38	fn from(value: PacketType) -> Self {
39		Self::from(&value)
40	}
41}
42
43impl TryFrom<i32> for PacketType {
44	type Error = MIONParamProtocolError;
45
46	fn try_from(value: i32) -> Result<Self, Self::Error> {
47		match value {
48			0 => Ok(Self::Read),
49			1 => Ok(Self::Write),
50			_ => Err(MIONParamProtocolError::PacketType(value)),
51		}
52	}
53}
54
55/// A request to dump all the 512 parameters available on the MION board.
56#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Valuable)]
57pub struct MionDumpParameters;
58
59impl MionDumpParameters {
60	/// Create a new request packet to send to a mion parameter port to tell it
61	/// to give us all the parameters.
62	#[must_use]
63	pub const fn new() -> Self {
64		Self {}
65	}
66}
67
68impl Default for MionDumpParameters {
69	fn default() -> Self {
70		Self::new()
71	}
72}
73
74impl Display for MionDumpParameters {
75	fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
76		write!(fmt, "MionDumpParameters")
77	}
78}
79
80impl TryFrom<Bytes> for MionDumpParameters {
81	type Error = NetworkParseError;
82
83	fn try_from(packet: Bytes) -> Result<Self, Self::Error> {
84		if packet.len() < 8 {
85			return Err(NetworkParseError::NotEnoughData(
86				"MionDumpParameters",
87				8,
88				packet.len(),
89				packet,
90			));
91		}
92		if packet.len() > 8 {
93			return Err(NetworkParseError::UnexpectedTrailer(
94				"MionDumpParameters",
95				packet.slice(8..),
96			));
97		}
98
99		// Header only -- read request id is 0, so 4 zeroes, and no body afterwards
100		// and no error status so 4 0's for length after.
101		let static_bytes: &'static [u8] = &[0_u8, 0, 0, 0, 0, 0, 0, 0];
102		if packet != static_bytes {
103			return Err(NetworkParseError::DoesntMatchStaticPayload(
104				"MionDumpParameters",
105				static_bytes,
106				packet,
107			));
108		}
109
110		Ok(Self)
111	}
112}
113
114impl From<&MionDumpParameters> for Bytes {
115	fn from(_: &MionDumpParameters) -> Self {
116		BytesMut::zeroed(8).freeze()
117	}
118}
119impl From<MionDumpParameters> for Bytes {
120	fn from(value: MionDumpParameters) -> Self {
121		Self::from(&value)
122	}
123}
124
125const DUMPED_MION_PARAMETERS_FIELDS: &[NamedField<'static>] = &[NamedField::new("parameters")];
126/// The response from a MION documenting all the parameters available on this board.
127#[derive(Clone, Debug, Hash, PartialEq, Eq)]
128pub struct DumpedMionParameters {
129	/// All of the parameters available on this board.
130	parameters: Bytes,
131}
132
133impl DumpedMionParameters {
134	/// Get the entire set of parameters for you to mess around with.
135	#[must_use]
136	pub const fn get_raw_parameters(&self) -> &Bytes {
137		&self.parameters
138	}
139
140	/// Get a parameter by a name.
141	///
142	/// ## Errors
143	///
144	/// - If the name of this parameter is not known.
145	pub fn get_parameter_by_name(&self, name: &str) -> Result<u8, MIONParameterAPIError> {
146		index_from_parameter_name(name)
147			.map(|index| self.parameters[index])
148			.ok_or_else(|| MIONParameterAPIError::NameNotKnown(name.to_owned()))
149	}
150
151	/// Get a parameter by a particular index.
152	///
153	/// ## Errors
154	///
155	/// - If the index is not within the range of valid parameters.
156	pub fn get_parameter_by_index(&self, index: usize) -> Result<u8, MIONParameterAPIError> {
157		if index > 511 {
158			return Err(MIONParameterAPIError::NotInRange(index));
159		}
160		Ok(self.parameters[index])
161	}
162}
163
164impl TryFrom<Bytes> for DumpedMionParameters {
165	type Error = NetworkParseError;
166
167	fn try_from(packet: Bytes) -> Result<Self, Self::Error> {
168		if packet.len() < 520 {
169			return Err(NetworkParseError::NotEnoughData(
170				"DumpedMionParameters",
171				520,
172				packet.len(),
173				packet,
174			));
175		}
176		if packet.len() > 520 {
177			return Err(NetworkParseError::UnexpectedTrailer(
178				"DumpedMionParameters",
179				packet.slice(520..),
180			));
181		}
182
183		let header = packet.slice(..8);
184		let packet_type = PacketType::try_from(i32::from_le_bytes([
185			header[0], header[1], header[2], header[3],
186		]))?;
187		if packet_type != PacketType::Read {
188			return Err(MIONParamProtocolError::PacketType(i32::from(packet_type)).into());
189		}
190		let size_or_error = i32::from_le_bytes([header[4], header[5], header[6], header[7]]);
191		if size_or_error != 512 {
192			return Err(MIONParamProtocolError::ErrorCode(size_or_error).into());
193		}
194		let parameters = packet.slice(8..);
195
196		Ok(Self { parameters })
197	}
198}
199
200impl From<&DumpedMionParameters> for Bytes {
201	fn from(value: &DumpedMionParameters) -> Self {
202		let mut buff = BytesMut::with_capacity(520);
203		buff.put_i32_le(i32::from(PacketType::Read));
204		// The size of parameters.
205		buff.put_i32_le(512);
206		buff.extend_from_slice(&value.parameters);
207		buff.freeze()
208	}
209}
210impl From<DumpedMionParameters> for Bytes {
211	fn from(value: DumpedMionParameters) -> Self {
212		Self::from(&value)
213	}
214}
215
216impl Structable for DumpedMionParameters {
217	fn definition(&self) -> StructDef<'_> {
218		StructDef::new_static(
219			"DumpedMionParameters",
220			Fields::Named(DUMPED_MION_PARAMETERS_FIELDS),
221		)
222	}
223}
224impl Valuable for DumpedMionParameters {
225	fn as_value(&self) -> Value<'_> {
226		Value::Structable(self)
227	}
228
229	fn visit(&self, visitor: &mut dyn valuable::Visit) {
230		let dump = ValuableParameterDump(&self.parameters);
231
232		visitor.visit_named_fields(&NamedValues::new(
233			DUMPED_MION_PARAMETERS_FIELDS,
234			&[Valuable::as_value(&dump)],
235		));
236	}
237}
238
239/// Set all of the parameters on a MION device.
240///
241/// There is no way to simply set one byte, you have to set them all at once.
242#[derive(Clone, Debug, Hash, PartialEq, Eq)]
243pub struct SetMionParameters {
244	parameters: Bytes,
245}
246
247impl SetMionParameters {
248	/// Create a new packet given a previously dumped header, and the new
249	/// complete set of parameters.
250	///
251	/// This will take a valid `dumped_header`, and will modify it into a
252	/// header for setting mion parameters.
253	///
254	/// ## Errors
255	///
256	/// - If the `parameters` argument is not exactly 512 bytes long.
257	pub fn new(parameters: Bytes) -> Result<Self, MIONParameterAPIError> {
258		if parameters.len() != 512 {
259			return Err(MIONParameterAPIError::BodyNotCorrectLength(
260				parameters.len(),
261			));
262		}
263
264		Ok(Self { parameters })
265	}
266
267	/// Get the raw parameter body, which contains the raw list of bytes.
268	#[must_use]
269	pub const fn get_raw_parameters(&self) -> &Bytes {
270		&self.parameters
271	}
272
273	/// Get a parameter by a name.
274	///
275	/// ## Errors
276	///
277	/// - If the name of this parameter is not known.
278	pub fn get_parameter_by_name(&self, name: &str) -> Result<u8, MIONParameterAPIError> {
279		index_from_parameter_name(name)
280			.map(|index| self.parameters[index])
281			.ok_or_else(|| MIONParameterAPIError::NameNotKnown(name.to_owned()))
282	}
283
284	/// Get a parameter by a particular index.
285	///
286	/// ## Errors
287	///
288	/// - If the index is not within the range of valid parameters.
289	pub fn get_parameter_by_index(&self, index: usize) -> Result<u8, MIONParameterAPIError> {
290		if index > 511 {
291			return Err(MIONParameterAPIError::NotInRange(index));
292		}
293		Ok(self.parameters[index])
294	}
295}
296
297impl TryFrom<Bytes> for SetMionParameters {
298	type Error = NetworkParseError;
299
300	fn try_from(packet: Bytes) -> Result<Self, Self::Error> {
301		if packet.len() < 520 {
302			return Err(NetworkParseError::NotEnoughData(
303				"SetMionParameters",
304				520,
305				packet.len(),
306				packet,
307			));
308		}
309		if packet.len() > 520 {
310			return Err(NetworkParseError::UnexpectedTrailer(
311				"SetMionParameters",
312				packet.slice(520..),
313			));
314		}
315
316		let header = packet.slice(..8);
317		let packet_type = PacketType::try_from(i32::from_le_bytes([
318			header[0], header[1], header[2], header[3],
319		]))?;
320		if packet_type != PacketType::Write {
321			return Err(MIONParamProtocolError::PacketType(i32::from(packet_type)).into());
322		}
323		let size_or_error_code = i32::from_le_bytes([header[4], header[5], header[6], header[7]]);
324		if size_or_error_code != 512 {
325			return Err(MIONParamProtocolError::ErrorCode(size_or_error_code).into());
326		}
327		let parameters = packet.slice(8..);
328
329		Ok(Self { parameters })
330	}
331}
332
333impl From<&SetMionParameters> for Bytes {
334	fn from(value: &SetMionParameters) -> Self {
335		let mut buff = BytesMut::with_capacity(520);
336		buff.put_i32_le(i32::from(PacketType::Write));
337		// The size of the body.
338		buff.put_i32_le(512);
339		buff.extend_from_slice(&value.parameters);
340		buff.freeze()
341	}
342}
343impl From<SetMionParameters> for Bytes {
344	fn from(value: SetMionParameters) -> Self {
345		Self::from(&value)
346	}
347}
348
349/// The response to a [`SetMionParameters`] being sent.
350#[derive(Clone, Debug, Hash, PartialEq, Eq)]
351pub struct SetMionParametersResponse {
352	/// The return code, anything that isn't a 0 should be considered an error.
353	return_code: i32,
354}
355
356impl SetMionParametersResponse {
357	/// Get the return code of this request.
358	///
359	/// 0 is used to indicate success, anything else is considered a failure.
360	#[must_use]
361	pub const fn get_return_code(&self) -> i32 {
362		self.return_code
363	}
364
365	/// If this operation can be considered a success.
366	#[must_use]
367	pub const fn is_success(&self) -> bool {
368		self.return_code == 0
369	}
370
371	/// If this operation error'd out, and was not successful.
372	#[must_use]
373	pub const fn is_error(&self) -> bool {
374		self.return_code != 0
375	}
376}
377
378impl TryFrom<Bytes> for SetMionParametersResponse {
379	type Error = NetworkParseError;
380
381	fn try_from(packet: Bytes) -> Result<Self, Self::Error> {
382		if packet.len() < 12 {
383			return Err(NetworkParseError::NotEnoughData(
384				"SetMionParametersResponse",
385				12,
386				packet.len(),
387				packet,
388			));
389		}
390		if packet.len() > 12 {
391			return Err(NetworkParseError::UnexpectedTrailer(
392				"SetMionParametersResponse",
393				packet.slice(12..),
394			));
395		}
396
397		let header = packet.slice(..8);
398		let packet_type = PacketType::try_from(i32::from_le_bytes([
399			header[0], header[1], header[2], header[3],
400		]))?;
401		if packet_type != PacketType::Write {
402			return Err(MIONParamProtocolError::PacketType(i32::from(packet_type)).into());
403		}
404		let size_or_status = i32::from_le_bytes([header[4], header[5], header[6], header[7]]);
405		if size_or_status != 4 {
406			return Err(MIONParamProtocolError::ErrorCode(size_or_status).into());
407		}
408
409		let body = packet.slice(8..);
410		let return_code = i32::from_le_bytes([body[0], body[1], body[2], body[3]]);
411
412		Ok(Self { return_code })
413	}
414}
415
416impl From<&SetMionParametersResponse> for Bytes {
417	fn from(value: &SetMionParametersResponse) -> Self {
418		let mut buff = BytesMut::with_capacity(12);
419		buff.put_i32_le(i32::from(PacketType::Write));
420		// Size of body.
421		buff.put_i32_le(4);
422		buff.put_i32_le(value.return_code);
423		buff.freeze()
424	}
425}
426impl From<SetMionParametersResponse> for Bytes {
427	fn from(value: SetMionParametersResponse) -> Self {
428		Self::from(&value)
429	}
430}
431
432#[cfg(test)]
433mod unit_tests {
434	use super::*;
435	use bytes::Bytes;
436	use std::sync::LazyLock;
437
438	static REAL_LIFE_DUMPED_MION_PARAMETERS_PACKET: LazyLock<Vec<u8>> = LazyLock::new(|| {
439		vec![
440			0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x0c, 0x0d,
441			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
442			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
443			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
444			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
445			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
446			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
447			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
448			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
449			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
450			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
451			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
452			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
453			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
454			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
455			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
456			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
457			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
458			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
459			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
460			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
461			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
462			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
463			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
464			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
465			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
466			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
467			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
468			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
469			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
470			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
471			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
472			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
473			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
474			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
475			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
476			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
477			0xff, 0xff,
478		]
479	});
480	static REAL_LIFE_SET_MION_PARAMETERS_PACKET: LazyLock<Vec<u8>> = LazyLock::new(|| {
481		vec![
482			0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x0c, 0x0d,
483			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
484			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
485			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
486			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
487			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
488			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
489			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
490			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
491			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
492			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
493			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
494			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
495			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
496			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
497			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
498			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
499			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
500			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
501			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
502			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
503			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
504			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
505			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
506			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
507			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
508			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
509			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
510			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
511			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
512			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
513			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
514			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
515			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
516			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
517			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
518			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
519			0xff, 0x45,
520		]
521	});
522
523	#[test]
524	pub fn deser_mion_dump_parameters() {
525		// Round-trip success.
526		{
527			assert!(
528				MionDumpParameters::try_from(Bytes::from(MionDumpParameters)).is_ok(),
529				"Deserializing a serialized MionDumpParameters was not a success!",
530			);
531		}
532
533		// Size Differences.
534		{
535			let short_data = vec![0x0; 4];
536			let too_much_data = vec![0x0; 16];
537
538			assert_eq!(
539				MionDumpParameters::try_from(Bytes::from(short_data.clone())),
540				Err(NetworkParseError::NotEnoughData(
541					"MionDumpParameters",
542					8,
543					short_data.len(),
544					Bytes::from(short_data),
545				)),
546			);
547
548			assert_eq!(
549				MionDumpParameters::try_from(Bytes::from(too_much_data.clone())),
550				Err(NetworkParseError::UnexpectedTrailer(
551					"MionDumpParameters",
552					Bytes::from(too_much_data).slice(8..),
553				)),
554			);
555		}
556
557		// Invalid packet contents
558		{
559			let invalid_static_packet = vec![0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0];
560			assert_eq!(
561				MionDumpParameters::try_from(Bytes::from(invalid_static_packet.clone())),
562				Err(NetworkParseError::DoesntMatchStaticPayload(
563					"MionDumpParameters",
564					&[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
565					Bytes::from(invalid_static_packet),
566				)),
567			);
568		}
569	}
570
571	#[test]
572	pub fn deser_dumped_mion_parameters() {
573		// Size Differences
574		{
575			let short_data = vec![0x0; 519];
576			let too_long_data = vec![0x0; 521];
577
578			assert_eq!(
579				DumpedMionParameters::try_from(Bytes::from(short_data.clone())),
580				Err(NetworkParseError::NotEnoughData(
581					"DumpedMionParameters",
582					520,
583					short_data.len(),
584					Bytes::from(short_data),
585				)),
586			);
587
588			assert_eq!(
589				DumpedMionParameters::try_from(Bytes::from(too_long_data.clone())),
590				Err(NetworkParseError::UnexpectedTrailer(
591					"DumpedMionParameters",
592					Bytes::from(too_long_data).slice(520..),
593				)),
594			);
595		}
596
597		// Invalid Command
598		{
599			// Bogus value.
600			let mut data = vec![0x0; 520];
601			data[0] = 11;
602			let packet_with_bad_data = Bytes::from(data);
603
604			assert_eq!(
605				DumpedMionParameters::try_from(packet_with_bad_data),
606				Err(MIONParamProtocolError::PacketType(11).into()),
607			);
608
609			// Write request, isn't valid either.
610			let mut data = vec![0x0; 520];
611			data[0] = 1;
612			let packet_with_bad_data = Bytes::from(data);
613
614			assert_eq!(
615				DumpedMionParameters::try_from(packet_with_bad_data),
616				Err(MIONParamProtocolError::PacketType(1).into()),
617			);
618		}
619
620		// Real life packet
621		{
622			let result = DumpedMionParameters::try_from(Bytes::from(
623				REAL_LIFE_DUMPED_MION_PARAMETERS_PACKET.clone(),
624			));
625			assert!(
626				result.is_ok(),
627				"Failed to parse a real life DumpedMionParameters packet:\n\n  {result:?}",
628			);
629			let result_two = DumpedMionParameters::try_from(Bytes::from(result.unwrap()));
630			assert!(
631				result_two.is_ok(),
632				"Failed to round-trip real life DumpedMionParameters:\n\n  {result_two:?}",
633			);
634		}
635	}
636
637	#[test]
638	pub fn dumped_mion_parameters_api() {
639		let parsed_packet = DumpedMionParameters::try_from(Bytes::from(
640			REAL_LIFE_DUMPED_MION_PARAMETERS_PACKET.clone(),
641		))
642		.expect("Failed to parse real life dumped mion parmaeters packet!");
643
644		assert_eq!(
645			parsed_packet.get_raw_parameters(),
646			&REAL_LIFE_DUMPED_MION_PARAMETERS_PACKET[8..],
647			".get_raw_parameters() for DumpedMionParameters did not return the correct body!",
648		);
649
650		assert_eq!(
651			parsed_packet.get_parameter_by_name("major-version"),
652			Ok(0x02),
653		);
654		assert_eq!(
655			parsed_packet.get_parameter_by_name("minor version"),
656			Ok(0x0C),
657		);
658		assert_eq!(
659			// Name also should accept indexes that are just a string.
660			parsed_packet.get_parameter_by_name("5"),
661			Ok(0x0D),
662		);
663		assert_eq!(
664			parsed_packet.get_parameter_by_name("512"),
665			Err(MIONParameterAPIError::NameNotKnown("512".to_owned())),
666		);
667
668		assert_eq!(parsed_packet.get_parameter_by_index(511), Ok(0xFF));
669		assert_eq!(
670			// 512 is out of bounds as we start counting at 0.
671			parsed_packet.get_parameter_by_index(512),
672			Err(MIONParameterAPIError::NotInRange(512)),
673		);
674	}
675
676	#[test]
677	pub fn deser_set_mion_parameters() {
678		// Size Differences
679		{
680			let short_data = vec![0x0; 519];
681			let too_long_data = vec![0x0; 521];
682
683			assert_eq!(
684				SetMionParameters::try_from(Bytes::from(short_data.clone())),
685				Err(NetworkParseError::NotEnoughData(
686					"SetMionParameters",
687					520,
688					short_data.len(),
689					Bytes::from(short_data),
690				)),
691			);
692
693			assert_eq!(
694				SetMionParameters::try_from(Bytes::from(too_long_data.clone())),
695				Err(NetworkParseError::UnexpectedTrailer(
696					"SetMionParameters",
697					Bytes::from(too_long_data).slice(520..),
698				)),
699			);
700		}
701
702		// Invalid Command
703		{
704			// Bogus value.
705			let mut data = vec![0x0; 520];
706			data[0] = 11;
707			let packet_with_bad_data = Bytes::from(data);
708
709			assert_eq!(
710				SetMionParameters::try_from(packet_with_bad_data),
711				Err(MIONParamProtocolError::PacketType(11).into()),
712			);
713
714			// Write request, isn't valid either.
715			let mut data = vec![0x0; 520];
716			data[0] = 0;
717			let packet_with_bad_data = Bytes::from(data);
718
719			assert_eq!(
720				SetMionParameters::try_from(packet_with_bad_data),
721				Err(MIONParamProtocolError::PacketType(0).into()),
722			);
723		}
724
725		// Real life packet
726		{
727			let result = SetMionParameters::try_from(Bytes::from(
728				REAL_LIFE_SET_MION_PARAMETERS_PACKET.clone(),
729			));
730			assert!(
731				result.is_ok(),
732				"Failed to parse a real life SetMionParameters packet:\n\n  {result:?}",
733			);
734			let result_two = SetMionParameters::try_from(Bytes::from(result.unwrap()));
735			assert!(
736				result_two.is_ok(),
737				"Failed to round-trip real life SetMionParameters:\n\n  {result_two:?}",
738			);
739		}
740	}
741
742	#[test]
743	pub fn set_mion_parameters_api() {
744		// Test creation APIs.
745		{
746			// We should always construct from a dumped packet, because we don't yet
747			// know what the header values are, and they could be important :)
748			assert!(
749				SetMionParameters::new(Bytes::from(&REAL_LIFE_DUMPED_MION_PARAMETERS_PACKET[8..]))
750					.is_ok(),
751				"Failed to construct `SetMionParameters` from a valid dumped parameters set!",
752			);
753
754			// Invalid parameters length
755			assert_eq!(
756				SetMionParameters::new(Bytes::from(&REAL_LIFE_DUMPED_MION_PARAMETERS_PACKET[7..])),
757				Err(MIONParameterAPIError::BodyNotCorrectLength(513)),
758			);
759		}
760
761		// Test non-creation APIs.
762		{
763			let parsed_packet = SetMionParameters::try_from(Bytes::from(
764				REAL_LIFE_SET_MION_PARAMETERS_PACKET.clone(),
765			))
766			.expect("Failed to parse real life set mion parameters packet!");
767
768			assert_eq!(
769				parsed_packet.get_raw_parameters(),
770				&REAL_LIFE_SET_MION_PARAMETERS_PACKET[8..],
771				".get_raw_parameters() for SetMionParameters did not return the correct body!",
772			);
773
774			assert_eq!(
775				parsed_packet.get_parameter_by_name("major-version"),
776				Ok(0x02),
777			);
778			assert_eq!(
779				parsed_packet.get_parameter_by_name("minor version"),
780				Ok(0x0C),
781			);
782			assert_eq!(
783				// Name also should accept indexes that are just a string.
784				parsed_packet.get_parameter_by_name("5"),
785				Ok(0x0D),
786			);
787			assert_eq!(
788				parsed_packet.get_parameter_by_name("512"),
789				Err(MIONParameterAPIError::NameNotKnown("512".to_owned())),
790			);
791
792			assert_eq!(parsed_packet.get_parameter_by_index(511), Ok(69));
793			assert_eq!(
794				// 512 is out of bounds as we start counting at 0.
795				parsed_packet.get_parameter_by_index(512),
796				Err(MIONParameterAPIError::NotInRange(512)),
797			);
798		}
799	}
800
801	#[test]
802	pub fn deser_set_mion_parameters_response() {
803		// Size Mismatch
804		{
805			let short_data = vec![0x0; 11];
806			let too_long_data = vec![0x0; 13];
807
808			assert_eq!(
809				SetMionParametersResponse::try_from(Bytes::from(short_data.clone())),
810				Err(NetworkParseError::NotEnoughData(
811					"SetMionParametersResponse",
812					12,
813					short_data.len(),
814					Bytes::from(short_data),
815				)),
816			);
817
818			assert_eq!(
819				SetMionParametersResponse::try_from(Bytes::from(too_long_data.clone())),
820				Err(NetworkParseError::UnexpectedTrailer(
821					"SetMionParametersResponse",
822					Bytes::from(too_long_data).slice(12..),
823				)),
824			);
825		}
826
827		// unknown packet type
828		{
829			// Bogus Packet Type
830			assert_eq!(
831				SetMionParametersResponse::try_from(Bytes::from(vec![
832					// Packet type -- bogus
833					0x11, 0x0, 0x0, 0x0, // Body length.
834					0x4, 0x0, 0x0, 0x0, // return code.
835					0x0, 0x0, 0x0, 0x0,
836				])),
837				Err(MIONParamProtocolError::PacketType(0x11).into()),
838			);
839
840			// Wrong Packet Type -- read instead of write
841			assert_eq!(
842				SetMionParametersResponse::try_from(Bytes::from(vec![
843					// Packet type -- read
844					0x0, 0x0, 0x0, 0x0, // Body length.
845					0x4, 0x0, 0x0, 0x0, // Return Code
846					0x0, 0x0, 0x0, 0x0,
847				])),
848				Err(MIONParamProtocolError::PacketType(0).into()),
849			);
850		}
851
852		// Bad Size/Status
853		{
854			assert_eq!(
855				SetMionParametersResponse::try_from(Bytes::from(vec![
856					// Packet type -- write
857					0x1, 0x0, 0x0, 0x0, // bogus size.
858					0x5, 0x0, 0x0, 0x0, // return code
859					0x0, 0x0, 0x0, 0x0,
860				])),
861				Err(MIONParamProtocolError::ErrorCode(5).into()),
862			);
863		}
864
865		// Successful -- includes roundtrip
866		{
867			let result = SetMionParametersResponse::try_from(Bytes::from(vec![
868				// Packet type -- write
869				0x1, 0x0, 0x0, 0x0, // Size -- correct.
870				0x4, 0x0, 0x0, 0x0, // Return code -- success!
871				0x0, 0x0, 0x0, 0x0,
872			]));
873			assert!(
874				result.is_ok(),
875				"Failed to respond to real life SetMionParametersResponse success!"
876			);
877			assert!(
878				SetMionParametersResponse::try_from(Bytes::from(result.unwrap())).is_ok(),
879				"Failed to round-trip real-life SetMionParametersResponse success!",
880			);
881		}
882	}
883}