named fields instead of tuple for Packet, doc adjustments
This commit is contained in:
		
							parent
							
								
									e54891e662
								
							
						
					
					
						commit
						e97418b51b
					
				
					 5 changed files with 529 additions and 135 deletions
				
			
		|  | @ -190,10 +190,17 @@ impl TryFrom<Packet> for Command { | |||
| 
 | ||||
|     /// Try to interpret the `Packet` as one containing a `Command`
 | ||||
|     fn try_from(packet: Packet) -> Result<Self, Self::Error> { | ||||
|         let Packet(Header(command_u16, a, _, _, _), _) = packet; | ||||
|         let command_code = match CommandCode::try_from(command_u16) { | ||||
|         let Packet { | ||||
|             header: Header { | ||||
|                 command_code, | ||||
|                 a, | ||||
|                 .. | ||||
|             }, | ||||
|             .. | ||||
|         } = packet; | ||||
|         let command_code = match CommandCode::try_from(command_code) { | ||||
|             Err(()) => { | ||||
|                 return Err(TryFromPacketError::InvalidCommand(command_u16)); | ||||
|                 return Err(TryFromPacketError::InvalidCommand(command_code)); | ||||
|             } | ||||
|             Ok(value) => value, | ||||
|         }; | ||||
|  | @ -266,8 +273,16 @@ impl Command { | |||
|         packet: Packet, | ||||
|         compression: CompressionCode, | ||||
|     ) -> Result<Command, TryFromPacketError> { | ||||
|         let Packet(Header(_, tiles_x, pixels_y, tile_w, pixel_h), payload) = | ||||
|             packet; | ||||
|         let Packet { | ||||
|             header: Header { | ||||
|                 command_code: _, | ||||
|                 a: tiles_x, | ||||
|                 b: pixels_y, | ||||
|                 c: tile_w, | ||||
|                 d: pixel_h, | ||||
|             }, | ||||
|             payload, | ||||
|         } = packet; | ||||
| 
 | ||||
|         let payload = match into_decompressed(compression, payload) { | ||||
|             None => return Err(TryFromPacketError::DecompressionFailed), | ||||
|  | @ -290,7 +305,16 @@ impl Command { | |||
|         packet: Packet, | ||||
|         command: Command, | ||||
|     ) -> Result<Command, TryFromPacketError> { | ||||
|         let Packet(Header(_, a, b, c, d), payload) = packet; | ||||
|         let Packet { | ||||
|             header: Header { | ||||
|                 command_code: _, | ||||
|                 a, | ||||
|                 b, | ||||
|                 c, | ||||
|                 d, | ||||
|             }, | ||||
|             payload, | ||||
|         } = packet; | ||||
|         if !payload.is_empty() { | ||||
|             Err(TryFromPacketError::UnexpectedPayloadSize(0, payload.len())) | ||||
|         } else if a != 0 || b != 0 || c != 0 || d != 0 { | ||||
|  | @ -304,7 +328,15 @@ impl Command { | |||
|     fn packet_into_linear_bitmap( | ||||
|         packet: Packet, | ||||
|     ) -> Result<(SpBitVec, CompressionCode), TryFromPacketError> { | ||||
|         let Packet(Header(_, _, length, sub, reserved), payload) = packet; | ||||
|         let Packet { | ||||
|             header: Header { | ||||
|                 b: length, | ||||
|                 c: sub, | ||||
|                 d: reserved, | ||||
|                 .. | ||||
|             }, | ||||
|             payload, | ||||
|         } = packet; | ||||
|         if reserved != 0 { | ||||
|             return Err(TryFromPacketError::ExtraneousHeaderValues); | ||||
|         } | ||||
|  | @ -330,7 +362,16 @@ impl Command { | |||
|     fn packet_into_char_brightness( | ||||
|         packet: &Packet, | ||||
|     ) -> Result<Command, TryFromPacketError> { | ||||
|         let Packet(Header(_, x, y, width, height), payload) = packet; | ||||
|         let Packet { | ||||
|             header: Header { | ||||
|                 command_code: _, | ||||
|                 a: x, | ||||
|                 b: y, | ||||
|                 c: width, | ||||
|                 d: height, | ||||
|             }, | ||||
|             payload, | ||||
|         } = packet; | ||||
| 
 | ||||
|         let grid = | ||||
|             PrimitiveGrid::load(*width as usize, *height as usize, payload); | ||||
|  | @ -348,7 +389,16 @@ impl Command { | |||
|     fn packet_into_brightness( | ||||
|         packet: &Packet, | ||||
|     ) -> Result<Command, TryFromPacketError> { | ||||
|         let Packet(Header(_, a, b, c, d), payload) = packet; | ||||
|         let Packet { | ||||
|             header: Header { | ||||
|                 command_code: _, | ||||
|                 a, | ||||
|                 b, | ||||
|                 c, | ||||
|                 d, | ||||
|             }, | ||||
|             payload, | ||||
|         } = packet; | ||||
|         if payload.len() != 1 { | ||||
|             return Err(TryFromPacketError::UnexpectedPayloadSize( | ||||
|                 1, | ||||
|  | @ -369,7 +419,16 @@ impl Command { | |||
|     fn packet_into_cp437( | ||||
|         packet: &Packet, | ||||
|     ) -> Result<Command, TryFromPacketError> { | ||||
|         let Packet(Header(_, a, b, c, d), payload) = packet; | ||||
|         let Packet { | ||||
|             header: Header { | ||||
|                 command_code: _, | ||||
|                 a, | ||||
|                 b, | ||||
|                 c, | ||||
|                 d, | ||||
|             }, | ||||
|             payload, | ||||
|         } = packet; | ||||
|         Ok(Command::Cp437Data( | ||||
|             Origin::new(*a as usize, *b as usize), | ||||
|             Cp437Grid::load(*c as usize, *d as usize, payload), | ||||
|  | @ -483,7 +542,16 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn error_invalid_command() { | ||||
|         let p = Packet(Header(0xFF, 0x00, 0x00, 0x00, 0x00), vec![]); | ||||
|         let p = Packet { | ||||
|             header: Header { | ||||
|                 command_code: 0xFF, | ||||
|                 a: 0x00, | ||||
|                 b: 0x00, | ||||
|                 c: 0x00, | ||||
|                 d: 0x00, | ||||
|             }, | ||||
|             payload: vec![], | ||||
|         }; | ||||
|         let result = Command::try_from(p); | ||||
|         assert!(matches!( | ||||
|             result, | ||||
|  | @ -493,10 +561,16 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn error_extraneous_header_values_clear() { | ||||
|         let p = Packet( | ||||
|             Header(CommandCode::Clear.into(), 0x05, 0x00, 0x00, 0x00), | ||||
|             vec![], | ||||
|         ); | ||||
|         let p = Packet { | ||||
|             header: Header { | ||||
|                 command_code: CommandCode::Clear.into(), | ||||
|                 a: 0x05, | ||||
|                 b: 0x00, | ||||
|                 c: 0x00, | ||||
|                 d: 0x00, | ||||
|             }, | ||||
|             payload: vec![], | ||||
|         }; | ||||
|         let result = Command::try_from(p); | ||||
|         assert!(matches!( | ||||
|             result, | ||||
|  | @ -506,10 +580,16 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn error_extraneous_header_values_brightness() { | ||||
|         let p = Packet( | ||||
|             Header(CommandCode::Brightness.into(), 0x00, 0x13, 0x37, 0x00), | ||||
|             vec![5], | ||||
|         ); | ||||
|         let p = Packet { | ||||
|             header: Header { | ||||
|                 command_code: CommandCode::Brightness.into(), | ||||
|                 a: 0x00, | ||||
|                 b: 0x13, | ||||
|                 c: 0x37, | ||||
|                 d: 0x00, | ||||
|             }, | ||||
|             payload: vec![5], | ||||
|         }; | ||||
|         let result = Command::try_from(p); | ||||
|         assert!(matches!( | ||||
|             result, | ||||
|  | @ -519,10 +599,16 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn error_extraneous_header_hard_reset() { | ||||
|         let p = Packet( | ||||
|             Header(CommandCode::HardReset.into(), 0x00, 0x00, 0x00, 0x01), | ||||
|             vec![], | ||||
|         ); | ||||
|         let p = Packet { | ||||
|             header: Header { | ||||
|                 command_code: CommandCode::HardReset.into(), | ||||
|                 a: 0x00, | ||||
|                 b: 0x00, | ||||
|                 c: 0x00, | ||||
|                 d: 0x01, | ||||
|             }, | ||||
|             payload: vec![], | ||||
|         }; | ||||
|         let result = Command::try_from(p); | ||||
|         assert!(matches!( | ||||
|             result, | ||||
|  | @ -532,10 +618,16 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn error_extraneous_header_fade_out() { | ||||
|         let p = Packet( | ||||
|             Header(CommandCode::FadeOut.into(), 0x10, 0x00, 0x00, 0x01), | ||||
|             vec![], | ||||
|         ); | ||||
|         let p = Packet { | ||||
|             header: Header { | ||||
|                 command_code: CommandCode::FadeOut.into(), | ||||
|                 a: 0x10, | ||||
|                 b: 0x00, | ||||
|                 c: 0x00, | ||||
|                 d: 0x01, | ||||
|             }, | ||||
|             payload: vec![], | ||||
|         }; | ||||
|         let result = Command::try_from(p); | ||||
|         assert!(matches!( | ||||
|             result, | ||||
|  | @ -545,10 +637,16 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn error_unexpected_payload() { | ||||
|         let p = Packet( | ||||
|             Header(CommandCode::FadeOut.into(), 0x00, 0x00, 0x00, 0x00), | ||||
|             vec![5, 7], | ||||
|         ); | ||||
|         let p = Packet { | ||||
|             header: Header { | ||||
|                 command_code: CommandCode::FadeOut.into(), | ||||
|                 a: 0x00, | ||||
|                 b: 0x00, | ||||
|                 c: 0x00, | ||||
|                 d: 0x00, | ||||
|             }, | ||||
|             payload: vec![5, 7], | ||||
|         }; | ||||
|         let result = Command::try_from(p); | ||||
|         assert!(matches!( | ||||
|             result, | ||||
|  | @ -564,15 +662,19 @@ mod tests { | |||
|                 PixelGrid::new(8, 8), | ||||
|                 compression, | ||||
|             ) | ||||
|             .into(); | ||||
|             let Packet(header, mut payload) = p; | ||||
|                 .into(); | ||||
| 
 | ||||
|             let Packet { | ||||
|                 header, | ||||
|                 mut payload, | ||||
|             } = p; | ||||
| 
 | ||||
|             // mangle it
 | ||||
|             for byte in payload.iter_mut() { | ||||
|                 *byte -= *byte / 2; | ||||
|             } | ||||
| 
 | ||||
|             let p = Packet(header, payload); | ||||
|             let p = Packet { header, payload }; | ||||
|             let result = Command::try_from(p); | ||||
|             if compression != CompressionCode::Uncompressed { | ||||
|                 assert_eq!(result, Err(TryFromPacketError::DecompressionFailed)) | ||||
|  | @ -590,15 +692,18 @@ mod tests { | |||
|                 BitVec::repeat(false, 8), | ||||
|                 compression, | ||||
|             ) | ||||
|             .into(); | ||||
|             let Packet(header, mut payload) = p; | ||||
|                 .into(); | ||||
|             let Packet { | ||||
|                 header, | ||||
|                 mut payload, | ||||
|             } = p; | ||||
| 
 | ||||
|             // mangle it
 | ||||
|             for byte in payload.iter_mut() { | ||||
|                 *byte -= *byte / 2; | ||||
|             } | ||||
| 
 | ||||
|             let p = Packet(header, payload); | ||||
|             let p = Packet { header, payload }; | ||||
|             let result = Command::try_from(p); | ||||
|             if compression != CompressionCode::Uncompressed { | ||||
|                 assert_eq!(result, Err(TryFromPacketError::DecompressionFailed)) | ||||
|  | @ -612,32 +717,59 @@ mod tests { | |||
|     #[test] | ||||
|     fn unexpected_payload_size_brightness() { | ||||
|         assert_eq!( | ||||
|             Command::try_from(Packet( | ||||
|                 Header(CommandCode::Brightness.into(), 0, 0, 0, 0), | ||||
|                 vec!(), | ||||
|             )), | ||||
|             Command::try_from(Packet { | ||||
|                 header: Header { | ||||
|                     command_code: CommandCode::Brightness.into(), | ||||
|                     a: 0, | ||||
|                     b: 0, | ||||
|                     c: 0, | ||||
|                     d: 0, | ||||
|                 }, | ||||
|                 payload: vec!() | ||||
|             }), | ||||
|             Err(TryFromPacketError::UnexpectedPayloadSize(1, 0)) | ||||
|         ); | ||||
| 
 | ||||
|         assert_eq!( | ||||
|             Command::try_from(Packet( | ||||
|                 Header(CommandCode::Brightness.into(), 0, 0, 0, 0), | ||||
|                 vec!(0, 0), | ||||
|             )), | ||||
|             Command::try_from(Packet { | ||||
|                 header: Header { | ||||
|                     command_code: CommandCode::Brightness.into(), | ||||
|                     a: 0, | ||||
|                     b: 0, | ||||
|                     c: 0, | ||||
|                     d: 0, | ||||
|                 }, | ||||
|                 payload: vec!(0, 0) | ||||
|             }), | ||||
|             Err(TryFromPacketError::UnexpectedPayloadSize(1, 2)) | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn error_reserved_used() { | ||||
|         let Packet(header, payload) = Command::BitmapLinear( | ||||
|         let Packet { header, payload } = Command::BitmapLinear( | ||||
|             0, | ||||
|             BitVec::repeat(false, 8), | ||||
|             CompressionCode::Uncompressed, | ||||
|         ) | ||||
|         .into(); | ||||
|         let Header(command, offset, length, sub, _reserved) = header; | ||||
|         let p = Packet(Header(command, offset, length, sub, 69), payload); | ||||
|             .into(); | ||||
|         let Header { | ||||
|             command_code: command, | ||||
|             a: offset, | ||||
|             b: length, | ||||
|             c: sub, | ||||
|             d: _reserved, | ||||
|         } = header; | ||||
|         let p = Packet { | ||||
|             header: Header { | ||||
|                 command_code: command, | ||||
|                 a: offset, | ||||
|                 b: length, | ||||
|                 c: sub, | ||||
|                 d: 69, | ||||
|             }, | ||||
|             payload, | ||||
|         }; | ||||
|         assert_eq!( | ||||
|             Command::try_from(p), | ||||
|             Err(TryFromPacketError::ExtraneousHeaderValues) | ||||
|  | @ -646,14 +778,29 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn error_invalid_compression() { | ||||
|         let Packet(header, payload) = Command::BitmapLinear( | ||||
|         let Packet { header, payload } = Command::BitmapLinear( | ||||
|             0, | ||||
|             BitVec::repeat(false, 8), | ||||
|             CompressionCode::Uncompressed, | ||||
|         ) | ||||
|         .into(); | ||||
|         let Header(command, offset, length, _sub, reserved) = header; | ||||
|         let p = Packet(Header(command, offset, length, 42, reserved), payload); | ||||
|             .into(); | ||||
|         let Header { | ||||
|             command_code: command, | ||||
|             a: offset, | ||||
|             b: length, | ||||
|             c: _sub, | ||||
|             d: reserved, | ||||
|         } = header; | ||||
|         let p = Packet { | ||||
|             header: Header { | ||||
|                 command_code: command, | ||||
|                 a: offset, | ||||
|                 b: length, | ||||
|                 c: 42, | ||||
|                 d: reserved, | ||||
|             }, | ||||
|             payload, | ||||
|         }; | ||||
|         assert_eq!( | ||||
|             Command::try_from(p), | ||||
|             Err(TryFromPacketError::InvalidCompressionCode(42)) | ||||
|  | @ -662,17 +809,29 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn error_unexpected_size() { | ||||
|         let Packet(header, payload) = Command::BitmapLinear( | ||||
|         let Packet { header, payload } = Command::BitmapLinear( | ||||
|             0, | ||||
|             BitVec::repeat(false, 8), | ||||
|             CompressionCode::Uncompressed, | ||||
|         ) | ||||
|         .into(); | ||||
|         let Header(command, offset, length, compression, reserved) = header; | ||||
|         let p = Packet( | ||||
|             Header(command, offset, 420, compression, reserved), | ||||
|             .into(); | ||||
|         let Header { | ||||
|             command_code: command, | ||||
|             a: offset, | ||||
|             b: length, | ||||
|             c: compression, | ||||
|             d: reserved, | ||||
|         } = header; | ||||
|         let p = Packet { | ||||
|             header: Header { | ||||
|                 command_code: command, | ||||
|                 a: offset, | ||||
|                 b: 420, | ||||
|                 c: compression, | ||||
|                 d: reserved, | ||||
|             }, | ||||
|             payload, | ||||
|         ); | ||||
|         }; | ||||
|         assert_eq!( | ||||
|             Command::try_from(p), | ||||
|             Err(TryFromPacketError::UnexpectedPayloadSize( | ||||
|  |  | |||
|  | @ -71,7 +71,7 @@ impl Connection { | |||
|             Connection::Udp(socket) => { | ||||
|                 socket | ||||
|                     .send(&data) | ||||
|                     .map_err(move |io_err| SendError::IoError(io_err)) | ||||
|                     .map_err(SendError::IoError) | ||||
|                     .map(move |_| ()) // ignore Ok value
 | ||||
|             } | ||||
|             Connection::Fake => Ok(()), | ||||
|  |  | |||
|  | @ -3,25 +3,84 @@ use std::mem::size_of; | |||
| use crate::command_code::CommandCode; | ||||
| use crate::compression::into_compressed; | ||||
| use crate::{ | ||||
|     Command, CompressionCode, Grid, Offset, Origin, PixelGrid, Pixels, | ||||
|     Command, CompressionCode, Grid, Offset, Origin, PixelGrid, Pixels, Tiles, | ||||
|     TILE_SIZE, | ||||
| }; | ||||
| 
 | ||||
| /// A raw header. Should probably not be used directly.
 | ||||
| /// A raw header.
 | ||||
| ///
 | ||||
| /// The header specifies the kind of command, the size of the payload and where to display the
 | ||||
| /// payload, where applicable.
 | ||||
| ///
 | ||||
| /// Because the meaning of most fields depend on the command, there are no speaking names for them.
 | ||||
| ///
 | ||||
| /// Should probably only be used directly to use features not exposed by the library.
 | ||||
| #[derive(Copy, Clone, Debug, PartialEq)] | ||||
| pub struct Header(pub u16, pub u16, pub u16, pub u16, pub u16); | ||||
| pub struct Header { | ||||
|     /// The first two bytes specify which command this packet represents.
 | ||||
|     pub command_code: u16, | ||||
|     /// First command-specific value
 | ||||
|     pub a: u16, | ||||
|     /// Second command-specific value
 | ||||
|     pub b: u16, | ||||
|     /// Third command-specific value
 | ||||
|     pub c: u16, | ||||
|     /// Fourth command-specific value
 | ||||
|     pub d: u16, | ||||
| } | ||||
| 
 | ||||
| /// The raw payload. Should probably not be used directly.
 | ||||
| /// The raw payload.
 | ||||
| ///
 | ||||
| /// Should probably only be used directly to use features not exposed by the library.
 | ||||
| pub type Payload = Vec<u8>; | ||||
| 
 | ||||
| /// The raw packet. Should probably not be used directly.
 | ||||
| /// The raw packet.
 | ||||
| ///
 | ||||
| /// Contents should probably only be used directly to use features not exposed by the library.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// Converting a packet to a command and back:
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{Command, Packet};
 | ||||
| /// # let command = Command::Clear;
 | ||||
| /// let packet: Packet = command.into();
 | ||||
| /// let command: Command = Command::try_from(packet).expect("could not read packet");
 | ||||
| /// ```
 | ||||
| ///
 | ||||
| /// Converting a packet to bytes and back:
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{Command, Packet};
 | ||||
| /// # let command = Command::Clear;
 | ||||
| /// # let packet: Packet = command.into();
 | ||||
| /// let bytes: Vec<u8> = packet.into();
 | ||||
| /// let packet = Packet::try_from(bytes).expect("could not read packet from bytes");
 | ||||
| /// ```
 | ||||
| ///
 | ||||
| #[derive(Clone, Debug, PartialEq)] | ||||
| pub struct Packet(pub Header, pub Payload); | ||||
| pub struct Packet { | ||||
|     /// Meta-information for the packed command
 | ||||
|     pub header: Header, | ||||
|     /// The data for the packed command
 | ||||
|     pub payload: Payload, | ||||
| } | ||||
| 
 | ||||
| impl From<Packet> for Vec<u8> { | ||||
|     /// Turn the packet into raw bytes ready to send
 | ||||
|     fn from(value: Packet) -> Self { | ||||
|         let Packet(Header(mode, a, b, c, d), payload) = value; | ||||
|         let Packet { | ||||
|             header: | ||||
|                 Header { | ||||
|                     command_code: mode, | ||||
|                     a, | ||||
|                     b, | ||||
|                     c, | ||||
|                     d, | ||||
|                 }, | ||||
|             payload, | ||||
|         } = value; | ||||
| 
 | ||||
|         let mut packet = vec![0u8; 10 + payload.len()]; | ||||
|         packet[0..=1].copy_from_slice(&u16::to_be_bytes(mode)); | ||||
|  | @ -36,13 +95,6 @@ impl From<Packet> for Vec<u8> { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| fn u16_from_be_slice(slice: &[u8]) -> u16 { | ||||
|     let mut bytes = [0u8; 2]; | ||||
|     bytes[0] = slice[0]; | ||||
|     bytes[1] = slice[1]; | ||||
|     u16::from_be_bytes(bytes) | ||||
| } | ||||
| 
 | ||||
| impl TryFrom<&[u8]> for Packet { | ||||
|     type Error = (); | ||||
| 
 | ||||
|  | @ -54,14 +106,31 @@ impl TryFrom<&[u8]> for Packet { | |||
|             return Err(()); | ||||
|         } | ||||
| 
 | ||||
|         let mode = u16_from_be_slice(&value[0..=1]); | ||||
|         let a = u16_from_be_slice(&value[2..=3]); | ||||
|         let b = u16_from_be_slice(&value[4..=5]); | ||||
|         let c = u16_from_be_slice(&value[6..=7]); | ||||
|         let d = u16_from_be_slice(&value[8..=9]); | ||||
|         let header = { | ||||
|             let command_code = Self::u16_from_be_slice(&value[0..=1]); | ||||
|             let a = Self::u16_from_be_slice(&value[2..=3]); | ||||
|             let b = Self::u16_from_be_slice(&value[4..=5]); | ||||
|             let c = Self::u16_from_be_slice(&value[6..=7]); | ||||
|             let d = Self::u16_from_be_slice(&value[8..=9]); | ||||
|             Header { | ||||
|                 command_code, | ||||
|                 a, | ||||
|                 b, | ||||
|                 c, | ||||
|                 d, | ||||
|             } | ||||
|         }; | ||||
|         let payload = value[10..].to_vec(); | ||||
| 
 | ||||
|         Ok(Packet(Header(mode, a, b, c, d), payload)) | ||||
|         Ok(Packet { header, payload }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl TryFrom<Vec<u8>> for Packet { | ||||
|     type Error = (); | ||||
| 
 | ||||
|     fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> { | ||||
|         Self::try_from(value.as_slice()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -79,26 +148,23 @@ impl From<Command> for Packet { | |||
|             Command::BitmapLegacy => { | ||||
|                 Self::command_code_only(CommandCode::BitmapLegacy) | ||||
|             } | ||||
|             Command::CharBrightness(origin, grid) => Packet( | ||||
|                 Header( | ||||
|                     CommandCode::CharBrightness.into(), | ||||
|                     origin.x as u16, | ||||
|                     origin.y as u16, | ||||
|                     grid.width() as u16, | ||||
|                     grid.height() as u16, | ||||
|                 ), | ||||
|                 grid.into(), | ||||
|             ), | ||||
|             Command::Brightness(brightness) => Packet( | ||||
|                 Header( | ||||
|                     CommandCode::Brightness.into(), | ||||
|                     0x00000, | ||||
|                     0x0000, | ||||
|                     0x0000, | ||||
|                     0x0000, | ||||
|                 ), | ||||
|                 vec![brightness.into()], | ||||
|             ), | ||||
|             Command::CharBrightness(origin, grid) => { | ||||
|                 Self::origin_grid_to_packet( | ||||
|                     origin, | ||||
|                     grid, | ||||
|                     CommandCode::CharBrightness, | ||||
|                 ) | ||||
|             } | ||||
|             Command::Brightness(brightness) => Packet { | ||||
|                 header: Header { | ||||
|                     command_code: CommandCode::Brightness.into(), | ||||
|                     a: 0x00000, | ||||
|                     b: 0x0000, | ||||
|                     c: 0x0000, | ||||
|                     d: 0x0000, | ||||
|                 }, | ||||
|                 payload: vec![brightness.into()], | ||||
|             }, | ||||
|             Command::BitmapLinearWin(origin, pixels, compression) => { | ||||
|                 Self::bitmap_win_into_packet(origin, pixels, compression) | ||||
|             } | ||||
|  | @ -134,15 +200,10 @@ impl From<Command> for Packet { | |||
|                     bits.into(), | ||||
|                 ) | ||||
|             } | ||||
|             Command::Cp437Data(origin, grid) => Packet( | ||||
|                 Header( | ||||
|                     CommandCode::Cp437Data.into(), | ||||
|                     origin.x as u16, | ||||
|                     origin.y as u16, | ||||
|                     grid.width() as u16, | ||||
|                     grid.height() as u16, | ||||
|                 ), | ||||
|                 grid.into(), | ||||
|             Command::Cp437Data(origin, grid) => Self::origin_grid_to_packet( | ||||
|                 origin, | ||||
|                 grid, | ||||
|                 CommandCode::Cp437Data, | ||||
|             ), | ||||
|         } | ||||
|     } | ||||
|  | @ -159,16 +220,16 @@ impl Packet { | |||
|     ) -> Packet { | ||||
|         let length = payload.len() as u16; | ||||
|         let payload = into_compressed(compression, payload); | ||||
|         Packet( | ||||
|             Header( | ||||
|                 command.into(), | ||||
|                 offset as u16, | ||||
|                 length, | ||||
|                 compression.into(), | ||||
|                 0, | ||||
|             ), | ||||
|         Packet { | ||||
|             header: Header { | ||||
|                 command_code: command.into(), | ||||
|                 a: offset as u16, | ||||
|                 b: length, | ||||
|                 c: compression.into(), | ||||
|                 d: 0, | ||||
|             }, | ||||
|             payload, | ||||
|         ) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     #[allow(clippy::cast_possible_truncation)] | ||||
|  | @ -198,15 +259,54 @@ impl Packet { | |||
|             CompressionCode::Zstd => CommandCode::BitmapLinearWinZstd, | ||||
|         }; | ||||
| 
 | ||||
|         Packet( | ||||
|             Header(command.into(), tile_x, origin.y as u16, tile_w, pixel_h), | ||||
|         Packet { | ||||
|             header: Header { | ||||
|                 command_code: command.into(), | ||||
|                 a: tile_x, | ||||
|                 b: origin.y as u16, | ||||
|                 c: tile_w, | ||||
|                 d: pixel_h, | ||||
|             }, | ||||
|             payload, | ||||
|         ) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Helper method for creating empty packets only containing the command code
 | ||||
|     fn command_code_only(code: CommandCode) -> Packet { | ||||
|         Packet(Header(code.into(), 0x0000, 0x0000, 0x0000, 0x0000), vec![]) | ||||
|         Packet { | ||||
|             header: Header { | ||||
|                 command_code: code.into(), | ||||
|                 a: 0x0000, | ||||
|                 b: 0x0000, | ||||
|                 c: 0x0000, | ||||
|                 d: 0x0000, | ||||
|             }, | ||||
|             payload: vec![], | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn u16_from_be_slice(slice: &[u8]) -> u16 { | ||||
|         let mut bytes = [0u8; 2]; | ||||
|         bytes[0] = slice[0]; | ||||
|         bytes[1] = slice[1]; | ||||
|         u16::from_be_bytes(bytes) | ||||
|     } | ||||
| 
 | ||||
|     fn origin_grid_to_packet<T>( | ||||
|         origin: Origin<Tiles>, | ||||
|         grid: impl Grid<T> + Into<Payload>, | ||||
|         command_code: CommandCode, | ||||
|     ) -> Packet { | ||||
|         Packet { | ||||
|             header: Header { | ||||
|                 command_code: command_code.into(), | ||||
|                 a: origin.x as u16, | ||||
|                 b: origin.y as u16, | ||||
|                 c: grid.width() as u16, | ||||
|                 d: grid.height() as u16, | ||||
|             }, | ||||
|             payload: grid.into(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -216,10 +316,31 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn round_trip() { | ||||
|         let p = Packet(Header(0, 1, 2, 3, 4), vec![42u8; 23]); | ||||
|         let p = Packet { | ||||
|             header: Header { | ||||
|                 command_code: 0, | ||||
|                 a: 1, | ||||
|                 b: 2, | ||||
|                 c: 3, | ||||
|                 d: 4, | ||||
|             }, | ||||
|             payload: vec![42u8; 23], | ||||
|         }; | ||||
|         let data: Vec<u8> = p.into(); | ||||
|         let p = Packet::try_from(&*data).unwrap(); | ||||
|         assert_eq!(p, Packet(Header(0, 1, 2, 3, 4), vec![42u8; 23])); | ||||
|         assert_eq!( | ||||
|             p, | ||||
|             Packet { | ||||
|                 header: Header { | ||||
|                     command_code: 0, | ||||
|                     a: 1, | ||||
|                     b: 2, | ||||
|                     c: 3, | ||||
|                     d: 4 | ||||
|                 }, | ||||
|                 payload: vec![42u8; 23] | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|  |  | |||
|  | @ -105,7 +105,7 @@ typedef struct SPBrightnessGrid SPBrightnessGrid; | |||
|  * | ||||
|  * This struct and associated functions implement the UDP protocol for the display. | ||||
|  * | ||||
|  * To send a `CCommand`, use a `CConnection`. | ||||
|  * To send a `SPCommand`, use a `SPConnection`. | ||||
|  * | ||||
|  * # Examples | ||||
|  * | ||||
|  | @ -539,6 +539,13 @@ size_t sp_brightness_grid_width(const struct SPBrightnessGrid *this_); | |||
|  * Allocates a new `Command::BitmapLinear` instance. | ||||
|  * The passed `BitVec` gets consumed. | ||||
|  * | ||||
|  * Set pixel data starting at the pixel offset on screen. | ||||
|  * | ||||
|  * The screen will continuously overwrite more pixel data without regarding the offset, meaning | ||||
|  * once the starting row is full, overwriting will continue on column 0. | ||||
|  * | ||||
|  * The contained `BitVec` is always uncompressed. | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  | @ -557,6 +564,13 @@ struct SPCommand *sp_command_bitmap_linear(SPOffset offset, | |||
|  * Allocates a new `Command::BitmapLinearAnd` instance. | ||||
|  * The passed `BitVec` gets consumed. | ||||
|  * | ||||
|  * Set pixel data according to an and-mask starting at the offset. | ||||
|  * | ||||
|  * The screen will continuously overwrite more pixel data without regarding the offset, meaning | ||||
|  * once the starting row is full, overwriting will continue on column 0. | ||||
|  * | ||||
|  * The contained `BitVec` is always uncompressed. | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  | @ -575,6 +589,13 @@ struct SPCommand *sp_command_bitmap_linear_and(SPOffset offset, | |||
|  * Allocates a new `Command::BitmapLinearOr` instance. | ||||
|  * The passed `BitVec` gets consumed. | ||||
|  * | ||||
|  * Set pixel data according to an or-mask starting at the offset. | ||||
|  * | ||||
|  * The screen will continuously overwrite more pixel data without regarding the offset, meaning | ||||
|  * once the starting row is full, overwriting will continue on column 0. | ||||
|  * | ||||
|  * The contained `BitVec` is always uncompressed. | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  | @ -593,6 +614,8 @@ struct SPCommand *sp_command_bitmap_linear_or(SPOffset offset, | |||
|  * Allocates a new `Command::BitmapLinearWin` instance. | ||||
|  * The passed `PixelGrid` gets consumed. | ||||
|  * | ||||
|  * Sets a window of pixels to the specified values | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  | @ -612,6 +635,13 @@ struct SPCommand *sp_command_bitmap_linear_win(size_t x, | |||
|  * Allocates a new `Command::BitmapLinearXor` instance. | ||||
|  * The passed `BitVec` gets consumed. | ||||
|  * | ||||
|  * Set pixel data according to a xor-mask starting at the offset. | ||||
|  * | ||||
|  * The screen will continuously overwrite more pixel data without regarding the offset, meaning | ||||
|  * once the starting row is full, overwriting will continue on column 0. | ||||
|  * | ||||
|  * The contained `BitVec` is always uncompressed. | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  | @ -647,6 +677,8 @@ struct SPCommand *sp_command_brightness(uint8_t brightness); | |||
|  * Allocates a new `Command::CharBrightness` instance. | ||||
|  * The passed `SPBrightnessGrid` gets consumed. | ||||
|  * | ||||
|  * Set the brightness of individual tiles in a rectangular area of the display. | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  | @ -663,6 +695,14 @@ struct SPCommand *sp_command_char_brightness(size_t x, | |||
| /**
 | ||||
|  * Allocates a new `Command::Clear` instance. | ||||
|  * | ||||
|  * Set all pixels to the off state. Does not affect brightness. | ||||
|  * | ||||
|  * # Examples | ||||
|  * | ||||
|  * ```C | ||||
|  * sp_connection_send(connection, sp_command_clear()); | ||||
|  * ``` | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  | @ -673,7 +713,7 @@ struct SPCommand *sp_command_char_brightness(size_t x, | |||
| struct SPCommand *sp_command_clear(void); | ||||
| 
 | ||||
| /**
 | ||||
|  * Clones a `Command` instance. | ||||
|  * Clones a `SPCommand` instance. | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  | @ -690,6 +730,13 @@ struct SPCommand *sp_command_clone(const struct SPCommand *original); | |||
|  * Allocates a new `Command::Cp437Data` instance. | ||||
|  * The passed `ByteGrid` gets consumed. | ||||
|  * | ||||
|  * Show text on the screen. | ||||
|  * | ||||
|  * <div class="warning"> | ||||
|  *     The library does not currently convert between UTF-8 and CP-437. | ||||
|  *     Because Rust expects UTF-8 strings, it might be necessary to only send ASCII for now. | ||||
|  * </div> | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  | @ -706,6 +753,13 @@ struct SPCommand *sp_command_cp437_data(size_t x, | |||
| /**
 | ||||
|  * Deallocates a `Command`. | ||||
|  * | ||||
|  * # Examples | ||||
|  * | ||||
|  * ```C | ||||
|  * SPCommand c = sp_command_clear(); | ||||
|  * sp_command_dealloc(c); | ||||
|  * ``` | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  | @ -731,6 +785,9 @@ struct SPCommand *sp_command_fade_out(void); | |||
| /**
 | ||||
|  * Allocates a new `Command::HardReset` instance. | ||||
|  * | ||||
|  * Kills the udp daemon on the display, which usually results in a restart. | ||||
|  * Please do not send this in your normal program flow. | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  | @ -741,18 +798,18 @@ struct SPCommand *sp_command_fade_out(void); | |||
| struct SPCommand *sp_command_hard_reset(void); | ||||
| 
 | ||||
| /**
 | ||||
|  * Tries to turn a `Packet` into a `Command`. The packet is deallocated in the process. | ||||
|  * Tries to turn a `SPPacket` into a `SPCommand`. The packet is deallocated in the process. | ||||
|  * | ||||
|  * Returns: pointer to new `Command` instance or NULL | ||||
|  * Returns: pointer to new `SPCommand` instance or NULL | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `packet` points to a valid instance of `Packet` | ||||
|  * - `packet` points to a valid instance of `SPPacket` | ||||
|  * - `packet` is not used concurrently or after this call | ||||
|  * - the result is checked for NULL | ||||
|  * - the returned `Command` instance is freed in some way, either by using a consuming function or | ||||
|  * - the returned `SPCommand` instance is freed in some way, either by using a consuming function or | ||||
|  *   by explicitly calling `sp_command_dealloc`. | ||||
|  */ | ||||
| struct SPCommand *sp_command_try_from_packet(struct SPPacket *packet); | ||||
|  |  | |||
|  | @ -546,25 +546,25 @@ namespace ServicePoint.BindGen | |||
|         public static extern ByteSlice sp_cp437_grid_unsafe_data_ref(Cp437Grid* @this); | ||||
| 
 | ||||
|         /// <summary> | ||||
|         ///  Tries to turn a `Packet` into a `Command`. The packet is deallocated in the process. | ||||
|         ///  Tries to turn a `SPPacket` into a `SPCommand`. The packet is deallocated in the process. | ||||
|         /// | ||||
|         ///  Returns: pointer to new `Command` instance or NULL | ||||
|         ///  Returns: pointer to new `SPCommand` instance or NULL | ||||
|         /// | ||||
|         ///  # Safety | ||||
|         /// | ||||
|         ///  The caller has to make sure that: | ||||
|         /// | ||||
|         ///  - `packet` points to a valid instance of `Packet` | ||||
|         ///  - `packet` points to a valid instance of `SPPacket` | ||||
|         ///  - `packet` is not used concurrently or after this call | ||||
|         ///  - the result is checked for NULL | ||||
|         ///  - the returned `Command` instance is freed in some way, either by using a consuming function or | ||||
|         ///  - the returned `SPCommand` instance is freed in some way, either by using a consuming function or | ||||
|         ///    by explicitly calling `sp_command_dealloc`. | ||||
|         /// </summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_command_try_from_packet", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern Command* sp_command_try_from_packet(Packet* packet); | ||||
| 
 | ||||
|         /// <summary> | ||||
|         ///  Clones a `Command` instance. | ||||
|         ///  Clones a `SPCommand` instance. | ||||
|         /// | ||||
|         ///  # Safety | ||||
|         /// | ||||
|  | @ -581,6 +581,14 @@ namespace ServicePoint.BindGen | |||
|         /// <summary> | ||||
|         ///  Allocates a new `Command::Clear` instance. | ||||
|         /// | ||||
|         ///  Set all pixels to the off state. Does not affect brightness. | ||||
|         /// | ||||
|         ///  # Examples | ||||
|         /// | ||||
|         ///  ```C | ||||
|         ///  sp_connection_send(connection, sp_command_clear()); | ||||
|         ///  ``` | ||||
|         /// | ||||
|         ///  # Safety | ||||
|         /// | ||||
|         ///  The caller has to make sure that: | ||||
|  | @ -594,6 +602,9 @@ namespace ServicePoint.BindGen | |||
|         /// <summary> | ||||
|         ///  Allocates a new `Command::HardReset` instance. | ||||
|         /// | ||||
|         ///  Kills the udp daemon on the display, which usually results in a restart. | ||||
|         ///  Please do not send this in your normal program flow. | ||||
|         /// | ||||
|         ///  # Safety | ||||
|         /// | ||||
|         ///  The caller has to make sure that: | ||||
|  | @ -639,6 +650,8 @@ namespace ServicePoint.BindGen | |||
|         ///  Allocates a new `Command::CharBrightness` instance. | ||||
|         ///  The passed `SPBrightnessGrid` gets consumed. | ||||
|         /// | ||||
|         ///  Set the brightness of individual tiles in a rectangular area of the display. | ||||
|         /// | ||||
|         ///  # Safety | ||||
|         /// | ||||
|         ///  The caller has to make sure that: | ||||
|  | @ -655,6 +668,13 @@ namespace ServicePoint.BindGen | |||
|         ///  Allocates a new `Command::BitmapLinear` instance. | ||||
|         ///  The passed `BitVec` gets consumed. | ||||
|         /// | ||||
|         ///  Set pixel data starting at the pixel offset on screen. | ||||
|         /// | ||||
|         ///  The screen will continuously overwrite more pixel data without regarding the offset, meaning | ||||
|         ///  once the starting row is full, overwriting will continue on column 0. | ||||
|         /// | ||||
|         ///  The contained `BitVec` is always uncompressed. | ||||
|         /// | ||||
|         ///  # Safety | ||||
|         /// | ||||
|         ///  The caller has to make sure that: | ||||
|  | @ -672,6 +692,13 @@ namespace ServicePoint.BindGen | |||
|         ///  Allocates a new `Command::BitmapLinearAnd` instance. | ||||
|         ///  The passed `BitVec` gets consumed. | ||||
|         /// | ||||
|         ///  Set pixel data according to an and-mask starting at the offset. | ||||
|         /// | ||||
|         ///  The screen will continuously overwrite more pixel data without regarding the offset, meaning | ||||
|         ///  once the starting row is full, overwriting will continue on column 0. | ||||
|         /// | ||||
|         ///  The contained `BitVec` is always uncompressed. | ||||
|         /// | ||||
|         ///  # Safety | ||||
|         /// | ||||
|         ///  The caller has to make sure that: | ||||
|  | @ -689,6 +716,13 @@ namespace ServicePoint.BindGen | |||
|         ///  Allocates a new `Command::BitmapLinearOr` instance. | ||||
|         ///  The passed `BitVec` gets consumed. | ||||
|         /// | ||||
|         ///  Set pixel data according to an or-mask starting at the offset. | ||||
|         /// | ||||
|         ///  The screen will continuously overwrite more pixel data without regarding the offset, meaning | ||||
|         ///  once the starting row is full, overwriting will continue on column 0. | ||||
|         /// | ||||
|         ///  The contained `BitVec` is always uncompressed. | ||||
|         /// | ||||
|         ///  # Safety | ||||
|         /// | ||||
|         ///  The caller has to make sure that: | ||||
|  | @ -706,6 +740,13 @@ namespace ServicePoint.BindGen | |||
|         ///  Allocates a new `Command::BitmapLinearXor` instance. | ||||
|         ///  The passed `BitVec` gets consumed. | ||||
|         /// | ||||
|         ///  Set pixel data according to a xor-mask starting at the offset. | ||||
|         /// | ||||
|         ///  The screen will continuously overwrite more pixel data without regarding the offset, meaning | ||||
|         ///  once the starting row is full, overwriting will continue on column 0. | ||||
|         /// | ||||
|         ///  The contained `BitVec` is always uncompressed. | ||||
|         /// | ||||
|         ///  # Safety | ||||
|         /// | ||||
|         ///  The caller has to make sure that: | ||||
|  | @ -723,6 +764,13 @@ namespace ServicePoint.BindGen | |||
|         ///  Allocates a new `Command::Cp437Data` instance. | ||||
|         ///  The passed `ByteGrid` gets consumed. | ||||
|         /// | ||||
|         ///  Show text on the screen. | ||||
|         /// | ||||
|         ///  <div class="warning"> | ||||
|         ///      The library does not currently convert between UTF-8 and CP-437. | ||||
|         ///      Because Rust expects UTF-8 strings, it might be necessary to only send ASCII for now. | ||||
|         ///  </div> | ||||
|         /// | ||||
|         ///  # Safety | ||||
|         /// | ||||
|         ///  The caller has to make sure that: | ||||
|  | @ -739,6 +787,8 @@ namespace ServicePoint.BindGen | |||
|         ///  Allocates a new `Command::BitmapLinearWin` instance. | ||||
|         ///  The passed `PixelGrid` gets consumed. | ||||
|         /// | ||||
|         ///  Sets a window of pixels to the specified values | ||||
|         /// | ||||
|         ///  # Safety | ||||
|         /// | ||||
|         ///  The caller has to make sure that: | ||||
|  | @ -755,6 +805,13 @@ namespace ServicePoint.BindGen | |||
|         /// <summary> | ||||
|         ///  Deallocates a `Command`. | ||||
|         /// | ||||
|         ///  # Examples | ||||
|         /// | ||||
|         ///  ```C | ||||
|         ///  SPCommand c = sp_command_clear(); | ||||
|         ///  sp_command_dealloc(c); | ||||
|         ///  ``` | ||||
|         /// | ||||
|         ///  # Safety | ||||
|         /// | ||||
|         ///  The caller has to make sure that: | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Vinzenz Schroeter
						Vinzenz Schroeter