use std::{ io::{ Cursor, Write, }, }; use crate::tlv; use thiserror::Error; const MAGIC_NUMBER: [u8; 4] = [0x9a, 0x4a, 0x43, 0x81]; pub const PACKET_SIZE: usize = 1024; type Mac = [u8; 6]; #[derive (Debug, PartialEq)] pub enum Message { // 1 Request1 { idem_id: [u8; 8], mac: Option <[u8; 6]> }, // 2 Response1 (Option <[u8; 6]>), // 3 Response2 (Response2), } #[derive (Debug, PartialEq)] pub struct Response2 { pub idem_id: [u8; 8], pub nickname: String, } #[derive (Debug, Error)] pub enum MessageError { #[error (transparent)] Io (#[from] std::io::Error), #[error ("Length prefix too long")] LengthPrefixTooLong ((usize, usize)), #[error (transparent)] Tlv (#[from] tlv::TlvError), #[error (transparent)] TryFromInt (#[from] std::num::TryFromIntError), #[error ("Unknown type")] UnknownType, #[error (transparent)] FromUtf8 (#[from] std::string::FromUtf8Error), } #[derive (Default)] struct DummyWriter { position: usize, } impl Write for DummyWriter { fn flush (&mut self) -> std::io::Result <()> { Ok (()) } fn write (&mut self, buf: &[u8]) -> std::io::Result { self.position += buf.len (); Ok (buf.len ()) } } impl Message { pub fn write (&self, w: &mut Cursor ) -> Result <(), std::io::Error> where Cursor : Write { match self { Self::Request1 { idem_id, mac, }=> { w.write_all (&[1])?; w.write_all (&idem_id[..])?; Self::write_mac_opt (w, *mac)?; }, Self::Response1 (mac) => { w.write_all (&[2])?; Self::write_mac_opt (w, *mac)?; }, Self::Response2 (x) => { w.write_all (&[3])?; // Measure length with dummy writes // This is dumb, I'm just messing around to see if I can do // this without allocating. let mut dummy_writer = DummyWriter::default (); Self::write_response_2 (&mut dummy_writer, x)?; // Write length and real params to real output let len = u32::try_from (dummy_writer.position).unwrap (); w.write_all (&len.to_le_bytes ())?; Self::write_response_2 (w, x)?; }, } Ok (()) } fn write_response_2 (w: &mut W, params: &Response2) -> Result <(), std::io::Error> { w.write_all (¶ms.idem_id)?; let nickname = params.nickname.as_bytes (); let nickname_len = u32::try_from (nickname.len ()).unwrap (); w.write_all (&nickname_len.to_le_bytes ())?; w.write_all (&nickname)?; Ok (()) } fn write_mac_opt (w: &mut W, mac: Option <[u8; 6]>) -> Result <(), std::io::Error> { match mac { Some (mac) => { w.write_all (&[1])?; w.write_all (&mac[..])?; }, None => w.write_all (&[0])?, } Ok (()) } pub fn to_vec (&self) -> Result , tlv::TlvError> { let mut cursor = Cursor::new (Vec::with_capacity (PACKET_SIZE)); cursor.write_all (&MAGIC_NUMBER)?; self.write (&mut cursor)?; Ok (cursor.into_inner ()) } pub fn many_to_vec (msgs: &[Self]) -> Result , tlv::TlvError> { let mut cursor = Cursor::new (Vec::with_capacity (PACKET_SIZE)); cursor.write_all (&MAGIC_NUMBER)?; for msg in msgs { msg.write (&mut cursor)?; } Ok (cursor.into_inner ()) } fn read1 (r: &mut R) -> Result { let t = tlv::Reader::u8 (r)?; Ok (match t { 1 => { let mut idem_id = [0u8; 8]; r.read_exact (&mut idem_id)?; let mac = Self::read_mac_opt (r)?; Self::Request1 { idem_id, mac, } }, 2 => { let mac = Self::read_mac_opt (r)?; Self::Response1 (mac) }, _ => return Err (MessageError::UnknownType), }) } fn read2 (r: &mut R) -> Result { let t = tlv::Reader::u8 (r)?; Ok (match t { 1 => { let mut idem_id = [0u8; 8]; r.read_exact (&mut idem_id)?; let mac = Self::read_mac_opt (r)?; Self::Request1 { idem_id, mac, } }, 2 => { let mac = Self::read_mac_opt (r)?; Self::Response1 (mac) }, 3 => { let mut len = [0; 4]; r.read_exact (&mut len)?; let _len = len; let mut idem_id = [0; 8]; r.read_exact (&mut idem_id)?; let mut nickname_len = [0; 4]; r.read_exact (&mut nickname_len)?; let nickname_len = u32::from_le_bytes (nickname_len); let nickname_len = usize::try_from (nickname_len)?; if nickname_len > 64 { return Err (MessageError::LengthPrefixTooLong ((64, nickname_len))); } let mut nickname = vec! [0u8; nickname_len]; r.read_exact (&mut nickname)?; let nickname = String::from_utf8 (nickname)?; Self::Response2 (Response2 { idem_id, nickname, }) }, _ => return Err (MessageError::UnknownType), }) } fn read_mac_opt (r: &mut R) -> Result