Compare commits
	
		
			5 commits
		
	
	
		
			08ed6a6fee
			...
			05ab631eb6
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
							 | 
						05ab631eb6 | ||
| 
							 | 
						bf2b320c81 | ||
| 
							 | 
						2d72ee05a7 | ||
| 
							 | 
						5e38ced392 | ||
| 
							 | 
						44fe6961e7 | 
					 31 changed files with 381 additions and 213 deletions
				
			
		| 
						 | 
				
			
			@ -40,8 +40,8 @@ fn main() {
 | 
			
		|||
 | 
			
		||||
    let text = cli.text.join("\n");
 | 
			
		||||
    let command = CharGridCommand {
 | 
			
		||||
        origin: Origin::ZERO,
 | 
			
		||||
        grid: CharGrid::wrap_str(TILE_WIDTH, &text),
 | 
			
		||||
        origin: Origin::ZERO,
 | 
			
		||||
    };
 | 
			
		||||
    connection.send(command).expect("sending text failed");
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,8 +18,8 @@ fn main() {
 | 
			
		|||
    bitmap.fill(true);
 | 
			
		||||
 | 
			
		||||
    let command = BitmapCommand {
 | 
			
		||||
        origin: Origin::ZERO,
 | 
			
		||||
        bitmap,
 | 
			
		||||
        origin: Origin::ZERO,
 | 
			
		||||
        compression: CompressionCode::default(),
 | 
			
		||||
    };
 | 
			
		||||
    connection.send(command).expect("send failed");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,8 +23,8 @@ fn main() {
 | 
			
		|||
 | 
			
		||||
    loop {
 | 
			
		||||
        let command = BitmapCommand {
 | 
			
		||||
            origin: Origin::ZERO,
 | 
			
		||||
            bitmap: field.clone(),
 | 
			
		||||
            origin: Origin::ZERO,
 | 
			
		||||
            compression: CompressionCode::default(),
 | 
			
		||||
        };
 | 
			
		||||
        connection.send(command).expect("could not send");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,8 +29,8 @@ fn main() {
 | 
			
		|||
        filled_grid.fill(true);
 | 
			
		||||
 | 
			
		||||
        let command = BitmapCommand {
 | 
			
		||||
            origin: Origin::ZERO,
 | 
			
		||||
            bitmap: filled_grid,
 | 
			
		||||
            origin: Origin::ZERO,
 | 
			
		||||
            compression: CompressionCode::default(),
 | 
			
		||||
        };
 | 
			
		||||
        connection.send(command).expect("send failed");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,8 +12,8 @@ fn main() {
 | 
			
		|||
    pixels.fill(true);
 | 
			
		||||
 | 
			
		||||
    let command = BitmapCommand {
 | 
			
		||||
        origin: Origin::ZERO,
 | 
			
		||||
        bitmap: pixels,
 | 
			
		||||
        origin: Origin::ZERO,
 | 
			
		||||
        compression: CompressionCode::default(),
 | 
			
		||||
    };
 | 
			
		||||
    connection.send(command).unwrap();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,8 +33,8 @@ fn main() {
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        let command = BitmapCommand {
 | 
			
		||||
            origin: Origin::ZERO,
 | 
			
		||||
            bitmap: enabled_pixels.clone(),
 | 
			
		||||
            origin: Origin::ZERO,
 | 
			
		||||
            compression: CompressionCode::default(),
 | 
			
		||||
        };
 | 
			
		||||
        connection
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,6 +18,7 @@ use rand::{
 | 
			
		|||
/// let result = connection.send(BrightnessCommand::from(b));
 | 
			
		||||
/// ```
 | 
			
		||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
 | 
			
		||||
#[repr(transparent)]
 | 
			
		||||
pub struct Brightness(u8);
 | 
			
		||||
 | 
			
		||||
impl From<Brightness> for u8 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,10 @@
 | 
			
		|||
use crate::{
 | 
			
		||||
    command_code::CommandCode, commands::TryFromPacketError,
 | 
			
		||||
    compression::into_compressed, compression::into_decompressed, Bitmap,
 | 
			
		||||
    CompressionCode, Grid, Header, Origin, Packet, Pixels, TypedCommand,
 | 
			
		||||
    TILE_SIZE,
 | 
			
		||||
    command_code::CommandCode,
 | 
			
		||||
    commands::errors::{TryFromPacketError, TryIntoPacketError},
 | 
			
		||||
    compression::into_compressed,
 | 
			
		||||
    compression::into_decompressed,
 | 
			
		||||
    Bitmap, CompressionCode, Grid, Header, Origin, Packet, Pixels,
 | 
			
		||||
    TypedCommand, TILE_SIZE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Overwrites a rectangular region of pixels.
 | 
			
		||||
| 
						 | 
				
			
			@ -28,25 +30,28 @@ use crate::{
 | 
			
		|||
///
 | 
			
		||||
/// connection.send(command).expect("send failed");
 | 
			
		||||
/// ```
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
			
		||||
pub struct BitmapCommand {
 | 
			
		||||
    /// where to start drawing the pixels
 | 
			
		||||
    pub origin: Origin<Pixels>,
 | 
			
		||||
    /// the pixels to send
 | 
			
		||||
    pub bitmap: Bitmap,
 | 
			
		||||
    /// where to start drawing the pixels
 | 
			
		||||
    pub origin: Origin<Pixels>,
 | 
			
		||||
    /// how to compress the command when converting to packet
 | 
			
		||||
    pub compression: CompressionCode,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<BitmapCommand> for Packet {
 | 
			
		||||
    fn from(value: BitmapCommand) -> Self {
 | 
			
		||||
impl TryFrom<BitmapCommand> for Packet {
 | 
			
		||||
    type Error = TryIntoPacketError;
 | 
			
		||||
 | 
			
		||||
    fn try_from(value: BitmapCommand) -> Result<Self, Self::Error> {
 | 
			
		||||
        assert_eq!(value.origin.x % 8, 0);
 | 
			
		||||
        assert_eq!(value.bitmap.width() % 8, 0);
 | 
			
		||||
 | 
			
		||||
        let tile_x = (value.origin.x / TILE_SIZE) as u16;
 | 
			
		||||
        let tile_w = (value.bitmap.width() / TILE_SIZE) as u16;
 | 
			
		||||
        let pixel_h = value.bitmap.height() as u16;
 | 
			
		||||
        let payload = into_compressed(value.compression, value.bitmap.into());
 | 
			
		||||
        let tile_x = (value.origin.x / TILE_SIZE).try_into()?;
 | 
			
		||||
        let tile_w = (value.bitmap.width() / TILE_SIZE).try_into()?;
 | 
			
		||||
        let pixel_h = value.bitmap.height().try_into()?;
 | 
			
		||||
        let payload = into_compressed(value.compression, value.bitmap.into())
 | 
			
		||||
            .ok_or(TryIntoPacketError::CompressionFailed)?;
 | 
			
		||||
        let command = match value.compression {
 | 
			
		||||
            CompressionCode::Uncompressed => {
 | 
			
		||||
                CommandCode::BitmapLinearWinUncompressed
 | 
			
		||||
| 
						 | 
				
			
			@ -61,16 +66,16 @@ impl From<BitmapCommand> for Packet {
 | 
			
		|||
            CompressionCode::Zstd => CommandCode::BitmapLinearWinZstd,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Packet {
 | 
			
		||||
        Ok(Packet {
 | 
			
		||||
            header: Header {
 | 
			
		||||
                command_code: command.into(),
 | 
			
		||||
                a: tile_x,
 | 
			
		||||
                b: value.origin.y as u16,
 | 
			
		||||
                b: value.origin.y.try_into()?,
 | 
			
		||||
                c: tile_w,
 | 
			
		||||
                d: pixel_h,
 | 
			
		||||
            },
 | 
			
		||||
            payload,
 | 
			
		||||
        }
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -162,8 +167,11 @@ impl BitmapCommand {
 | 
			
		|||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::command_code::CommandCode;
 | 
			
		||||
    use crate::commands::tests::TestImplementsCommand;
 | 
			
		||||
    use crate::*;
 | 
			
		||||
 | 
			
		||||
    impl TestImplementsCommand for BitmapCommand {}
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn command_code() {
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
| 
						 | 
				
			
			@ -188,7 +196,8 @@ mod tests {
 | 
			
		|||
                bitmap: Bitmap::new(8, 8).unwrap(),
 | 
			
		||||
                compression: *compression,
 | 
			
		||||
            }
 | 
			
		||||
            .into();
 | 
			
		||||
            .try_into()
 | 
			
		||||
            .unwrap();
 | 
			
		||||
 | 
			
		||||
            let Packet {
 | 
			
		||||
                header,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
use crate::{
 | 
			
		||||
    command_code::CommandCode, commands::check_command_code_only,
 | 
			
		||||
    commands::TryFromPacketError, Packet, TypedCommand,
 | 
			
		||||
    commands::errors::TryFromPacketError, Packet, TypedCommand,
 | 
			
		||||
};
 | 
			
		||||
use std::fmt::Debug;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -17,7 +17,7 @@ use std::fmt::Debug;
 | 
			
		|||
/// # #[allow(deprecated)]
 | 
			
		||||
/// connection.send(BitmapLegacyCommand).unwrap();
 | 
			
		||||
/// ```
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
			
		||||
#[deprecated]
 | 
			
		||||
pub struct BitmapLegacyCommand;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -54,8 +54,12 @@ impl From<BitmapLegacyCommand> for TypedCommand {
 | 
			
		|||
#[allow(deprecated)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::commands::tests::round_trip;
 | 
			
		||||
    use crate::Header;
 | 
			
		||||
    use crate::{
 | 
			
		||||
        commands::tests::{round_trip, TestImplementsCommand},
 | 
			
		||||
        Header
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    impl TestImplementsCommand for BitmapLegacyCommand {}
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn invalid_fields() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,7 @@
 | 
			
		|||
use crate::compression::into_compressed;
 | 
			
		||||
use crate::{
 | 
			
		||||
    command_code::CommandCode, commands::TryFromPacketError,
 | 
			
		||||
    compression::into_decompressed, BitVec, CompressionCode, Header, Offset,
 | 
			
		||||
    Packet, TypedCommand,
 | 
			
		||||
    command_code::CommandCode, commands::errors::TryFromPacketError,
 | 
			
		||||
    compression::into_compressed, compression::into_decompressed, BitVec,
 | 
			
		||||
    CompressionCode, Header, Offset, Packet, TryIntoPacketError, TypedCommand,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Binary operations for use with the [BitVecCommand] command.
 | 
			
		||||
| 
						 | 
				
			
			@ -31,40 +30,43 @@ pub enum BinaryOperation {
 | 
			
		|||
/// For example, [BinaryOperation::Or] can be used to turn on some pixels without affecting other pixels.
 | 
			
		||||
///
 | 
			
		||||
/// The contained [BitVec] is always uncompressed.
 | 
			
		||||
#[derive(Clone, PartialEq, Debug)]
 | 
			
		||||
#[derive(Clone, PartialEq, Debug, Eq)]
 | 
			
		||||
pub struct BitVecCommand {
 | 
			
		||||
    /// where to start overwriting pixel data
 | 
			
		||||
    pub offset: Offset,
 | 
			
		||||
    /// the pixels to send to the display as one long row
 | 
			
		||||
    pub bitvec: BitVec,
 | 
			
		||||
    /// where to start overwriting pixel data
 | 
			
		||||
    pub offset: Offset,
 | 
			
		||||
    /// The operation to apply on the display per bit comparing old and new state.
 | 
			
		||||
    pub operation: BinaryOperation,
 | 
			
		||||
    /// how to compress the command when converting to packet
 | 
			
		||||
    pub compression: CompressionCode,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<BitVecCommand> for Packet {
 | 
			
		||||
    fn from(command: BitVecCommand) -> Self {
 | 
			
		||||
        let command_code = match command.operation {
 | 
			
		||||
impl TryFrom<BitVecCommand> for Packet {
 | 
			
		||||
    type Error = TryIntoPacketError;
 | 
			
		||||
 | 
			
		||||
    fn try_from(value: BitVecCommand) -> Result<Self, Self::Error> {
 | 
			
		||||
        let command_code = match value.operation {
 | 
			
		||||
            BinaryOperation::Overwrite => CommandCode::BitmapLinear,
 | 
			
		||||
            BinaryOperation::And => CommandCode::BitmapLinearAnd,
 | 
			
		||||
            BinaryOperation::Or => CommandCode::BitmapLinearOr,
 | 
			
		||||
            BinaryOperation::Xor => CommandCode::BitmapLinearXor,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let payload: Vec<_> = command.bitvec.into();
 | 
			
		||||
        let length = payload.len() as u16;
 | 
			
		||||
        let payload = into_compressed(command.compression, payload);
 | 
			
		||||
        Packet {
 | 
			
		||||
        let payload: Vec<_> = value.bitvec.into();
 | 
			
		||||
        let length = payload.len().try_into()?;
 | 
			
		||||
        let payload = into_compressed(value.compression, payload)
 | 
			
		||||
            .ok_or(TryIntoPacketError::CompressionFailed)?;
 | 
			
		||||
        Ok(Packet {
 | 
			
		||||
            header: Header {
 | 
			
		||||
                command_code: command_code.into(),
 | 
			
		||||
                a: command.offset as u16,
 | 
			
		||||
                a: value.offset.try_into()?,
 | 
			
		||||
                b: length,
 | 
			
		||||
                c: command.compression.into(),
 | 
			
		||||
                c: value.compression.into(),
 | 
			
		||||
                d: 0,
 | 
			
		||||
            },
 | 
			
		||||
            payload,
 | 
			
		||||
        }
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -135,9 +137,11 @@ impl From<BitVecCommand> for TypedCommand {
 | 
			
		|||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::commands::tests::round_trip;
 | 
			
		||||
    use crate::commands::tests::{round_trip, TestImplementsCommand};
 | 
			
		||||
    use crate::{commands, Bitmap, BitmapCommand, Origin};
 | 
			
		||||
 | 
			
		||||
    impl TestImplementsCommand for BitVecCommand {}
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn command_code() {
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
| 
						 | 
				
			
			@ -193,7 +197,8 @@ mod tests {
 | 
			
		|||
                compression: *compression,
 | 
			
		||||
                operation: BinaryOperation::Overwrite,
 | 
			
		||||
            }
 | 
			
		||||
            .into();
 | 
			
		||||
            .try_into()
 | 
			
		||||
            .unwrap();
 | 
			
		||||
            let Packet {
 | 
			
		||||
                header,
 | 
			
		||||
                mut payload,
 | 
			
		||||
| 
						 | 
				
			
			@ -223,7 +228,8 @@ mod tests {
 | 
			
		|||
            compression: CompressionCode::Uncompressed,
 | 
			
		||||
            operation: BinaryOperation::Or,
 | 
			
		||||
        }
 | 
			
		||||
        .into();
 | 
			
		||||
        .try_into()
 | 
			
		||||
        .unwrap();
 | 
			
		||||
        let Header {
 | 
			
		||||
            command_code: command,
 | 
			
		||||
            a: offset,
 | 
			
		||||
| 
						 | 
				
			
			@ -255,7 +261,8 @@ mod tests {
 | 
			
		|||
            compression: CompressionCode::Uncompressed,
 | 
			
		||||
            operation: BinaryOperation::And,
 | 
			
		||||
        }
 | 
			
		||||
        .into();
 | 
			
		||||
        .try_into()
 | 
			
		||||
        .unwrap();
 | 
			
		||||
        let Header {
 | 
			
		||||
            command_code: command,
 | 
			
		||||
            a: offset,
 | 
			
		||||
| 
						 | 
				
			
			@ -287,7 +294,8 @@ mod tests {
 | 
			
		|||
            compression: CompressionCode::Uncompressed,
 | 
			
		||||
            operation: BinaryOperation::Xor,
 | 
			
		||||
        }
 | 
			
		||||
        .into();
 | 
			
		||||
        .try_into()
 | 
			
		||||
        .unwrap();
 | 
			
		||||
        let Header {
 | 
			
		||||
            command_code: command,
 | 
			
		||||
            a: offset,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
use crate::{
 | 
			
		||||
    command_code::CommandCode, commands::check_command_code,
 | 
			
		||||
    commands::TryFromPacketError, Brightness, Header, Packet, TypedCommand,
 | 
			
		||||
    commands::errors::TryFromPacketError, Brightness, Header, Packet,
 | 
			
		||||
    TypedCommand,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Set the brightness of all tiles to the same value.
 | 
			
		||||
| 
						 | 
				
			
			@ -13,7 +14,7 @@ use crate::{
 | 
			
		|||
/// let command = BrightnessCommand { brightness: Brightness::MAX };
 | 
			
		||||
/// connection.send(command).unwrap();
 | 
			
		||||
/// ```
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
			
		||||
pub struct BrightnessCommand {
 | 
			
		||||
    /// the brightness to set all pixels to
 | 
			
		||||
    pub brightness: Brightness,
 | 
			
		||||
| 
						 | 
				
			
			@ -85,12 +86,14 @@ impl From<Brightness> for BrightnessCommand {
 | 
			
		|||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use crate::command_code::CommandCode;
 | 
			
		||||
    use crate::commands::tests::round_trip;
 | 
			
		||||
    use crate::commands::errors::TryFromPacketError;
 | 
			
		||||
    use crate::commands::tests::{round_trip, TestImplementsCommand};
 | 
			
		||||
    use crate::{
 | 
			
		||||
        commands, Brightness, BrightnessCommand, Header, Packet,
 | 
			
		||||
        TryFromPacketError, TypedCommand,
 | 
			
		||||
        commands, Brightness, BrightnessCommand, Header, Packet, TypedCommand,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    impl TestImplementsCommand for BrightnessCommand {}
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn brightness_as_command() {
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,25 +1,27 @@
 | 
			
		|||
use crate::{
 | 
			
		||||
    command_code::CommandCode, commands::check_command_code,
 | 
			
		||||
    commands::TryFromPacketError, BrightnessGrid, ByteGrid, Header, Origin,
 | 
			
		||||
    Packet, Tiles, TypedCommand,
 | 
			
		||||
    commands::errors::TryFromPacketError, BrightnessGrid, ByteGrid, Header,
 | 
			
		||||
    Origin, Packet, Tiles, TryIntoPacketError, TypedCommand,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Set the brightness of individual tiles in a rectangular area of the display.
 | 
			
		||||
#[derive(Clone, PartialEq, Debug)]
 | 
			
		||||
#[derive(Clone, PartialEq, Debug, Eq)]
 | 
			
		||||
pub struct BrightnessGridCommand {
 | 
			
		||||
    /// which tile the brightness rectangle should start
 | 
			
		||||
    pub origin: Origin<Tiles>,
 | 
			
		||||
    /// the brightness values per tile
 | 
			
		||||
    pub grid: BrightnessGrid,
 | 
			
		||||
    /// which tile the brightness rectangle should start
 | 
			
		||||
    pub origin: Origin<Tiles>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<BrightnessGridCommand> for Packet {
 | 
			
		||||
    fn from(value: BrightnessGridCommand) -> Self {
 | 
			
		||||
        Packet::origin_grid_to_packet(
 | 
			
		||||
impl TryFrom<BrightnessGridCommand> for Packet {
 | 
			
		||||
    type Error = TryIntoPacketError;
 | 
			
		||||
 | 
			
		||||
    fn try_from(value: BrightnessGridCommand) -> Result<Self, Self::Error> {
 | 
			
		||||
        Ok(Packet::origin_grid_to_packet(
 | 
			
		||||
            value.origin,
 | 
			
		||||
            value.grid,
 | 
			
		||||
            CommandCode::CharBrightness,
 | 
			
		||||
        )
 | 
			
		||||
        )?)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -74,12 +76,15 @@ impl From<BrightnessGridCommand> for TypedCommand {
 | 
			
		|||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use crate::commands::tests::round_trip;
 | 
			
		||||
    use crate::commands::errors::TryFromPacketError;
 | 
			
		||||
    use crate::commands::tests::{round_trip, TestImplementsCommand};
 | 
			
		||||
    use crate::{
 | 
			
		||||
        commands, BrightnessGrid, BrightnessGridCommand, Origin, Packet,
 | 
			
		||||
        TryFromPacketError, TypedCommand,
 | 
			
		||||
        TypedCommand,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    impl TestImplementsCommand for BrightnessGridCommand {}
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn round_trip_char_brightness() {
 | 
			
		||||
        round_trip(
 | 
			
		||||
| 
						 | 
				
			
			@ -98,7 +103,7 @@ mod tests {
 | 
			
		|||
            origin: Origin::ZERO,
 | 
			
		||||
            grid,
 | 
			
		||||
        };
 | 
			
		||||
        let mut packet: Packet = command.into();
 | 
			
		||||
        let mut packet: Packet = command.try_into().unwrap();
 | 
			
		||||
        let slot = packet.payload.get_mut(1).unwrap();
 | 
			
		||||
        *slot = 23;
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
use crate::{
 | 
			
		||||
    command_code::CommandCode, commands::check_command_code,
 | 
			
		||||
    commands::TryFromPacketError, CharGrid, Header, Origin, Packet, Tiles,
 | 
			
		||||
    TypedCommand,
 | 
			
		||||
    commands::errors::TryFromPacketError, CharGrid, Header, Origin, Packet,
 | 
			
		||||
    Tiles, TryIntoPacketError, TypedCommand,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Show text on the screen.
 | 
			
		||||
| 
						 | 
				
			
			@ -16,21 +16,23 @@ use crate::{
 | 
			
		|||
/// let grid = CharGrid::from("Hello,\nWorld!");
 | 
			
		||||
/// connection.send(CharGridCommand { origin: Origin::ZERO, grid }).expect("send failed");
 | 
			
		||||
/// ```
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
			
		||||
pub struct CharGridCommand {
 | 
			
		||||
    /// which tile the text should start
 | 
			
		||||
    pub origin: Origin<Tiles>,
 | 
			
		||||
    /// the text to send to the display
 | 
			
		||||
    pub grid: CharGrid,
 | 
			
		||||
    /// which tile the text should start on
 | 
			
		||||
    pub origin: Origin<Tiles>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<CharGridCommand> for Packet {
 | 
			
		||||
    fn from(value: CharGridCommand) -> Self {
 | 
			
		||||
        Packet::origin_grid_to_packet(
 | 
			
		||||
impl TryFrom<CharGridCommand> for Packet {
 | 
			
		||||
    type Error = TryIntoPacketError;
 | 
			
		||||
 | 
			
		||||
    fn try_from(value: CharGridCommand) -> Result<Self, Self::Error> {
 | 
			
		||||
        Ok(Packet::origin_grid_to_packet(
 | 
			
		||||
            value.origin,
 | 
			
		||||
            value.grid,
 | 
			
		||||
            CommandCode::Utf8Data,
 | 
			
		||||
        )
 | 
			
		||||
        )?)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -82,9 +84,11 @@ impl From<CharGridCommand> for TypedCommand {
 | 
			
		|||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use crate::commands::tests::round_trip;
 | 
			
		||||
    use crate::commands::tests::{round_trip, TestImplementsCommand};
 | 
			
		||||
    use crate::{CharGrid, CharGridCommand, Origin};
 | 
			
		||||
 | 
			
		||||
    impl TestImplementsCommand for CharGridCommand {}
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn round_trip_utf8_data() {
 | 
			
		||||
        round_trip(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
use crate::{
 | 
			
		||||
    command_code::CommandCode, commands::check_command_code_only,
 | 
			
		||||
    commands::TryFromPacketError, Packet, TypedCommand,
 | 
			
		||||
    commands::errors::TryFromPacketError, Packet, TypedCommand,
 | 
			
		||||
};
 | 
			
		||||
use std::fmt::Debug;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -12,7 +12,7 @@ use std::fmt::Debug;
 | 
			
		|||
/// # use servicepoint::*;
 | 
			
		||||
/// # let connection = FakeConnection;
 | 
			
		||||
/// connection.send(ClearCommand).unwrap();
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
			
		||||
/// ```
 | 
			
		||||
pub struct ClearCommand;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -43,8 +43,11 @@ impl From<ClearCommand> for TypedCommand {
 | 
			
		|||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::commands::tests::TestImplementsCommand;
 | 
			
		||||
    use crate::Header;
 | 
			
		||||
 | 
			
		||||
    impl TestImplementsCommand for ClearCommand {}
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn round_trip() {
 | 
			
		||||
        crate::commands::tests::round_trip(ClearCommand.into());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
use crate::{
 | 
			
		||||
    command_code::CommandCode, commands::check_command_code,
 | 
			
		||||
    commands::TryFromPacketError, Cp437Grid, Header, Origin, Packet, Tiles,
 | 
			
		||||
    TypedCommand,
 | 
			
		||||
    commands::errors::TryFromPacketError, Cp437Grid, Header, Origin, Packet,
 | 
			
		||||
    Tiles, TryIntoPacketError, TypedCommand,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Show text on the screen.
 | 
			
		||||
| 
						 | 
				
			
			@ -27,21 +27,23 @@ use crate::{
 | 
			
		|||
/// connection.send(Cp437GridCommand{ origin: Origin::new(2, 2), grid }).unwrap();
 | 
			
		||||
/// ```
 | 
			
		||||
/// [CP-437]: https://en.wikipedia.org/wiki/Code_page_437
 | 
			
		||||
#[derive(Clone, Debug, PartialEq)]
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, Eq)]
 | 
			
		||||
pub struct Cp437GridCommand {
 | 
			
		||||
    /// which tile the text should start
 | 
			
		||||
    pub origin: Origin<Tiles>,
 | 
			
		||||
    /// the text to send to the display
 | 
			
		||||
    pub grid: Cp437Grid,
 | 
			
		||||
    /// which tile the text should start
 | 
			
		||||
    pub origin: Origin<Tiles>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<Cp437GridCommand> for Packet {
 | 
			
		||||
    fn from(value: Cp437GridCommand) -> Self {
 | 
			
		||||
        Packet::origin_grid_to_packet(
 | 
			
		||||
impl TryFrom<Cp437GridCommand> for Packet {
 | 
			
		||||
    type Error = TryIntoPacketError;
 | 
			
		||||
 | 
			
		||||
    fn try_from(value: Cp437GridCommand) -> Result<Self, Self::Error> {
 | 
			
		||||
        Ok(Packet::origin_grid_to_packet(
 | 
			
		||||
            value.origin,
 | 
			
		||||
            value.grid,
 | 
			
		||||
            CommandCode::Cp437Data,
 | 
			
		||||
        )
 | 
			
		||||
        )?)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -91,7 +93,9 @@ impl From<Cp437GridCommand> for TypedCommand {
 | 
			
		|||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::commands::tests::round_trip;
 | 
			
		||||
    use crate::commands::tests::{round_trip, TestImplementsCommand};
 | 
			
		||||
 | 
			
		||||
    impl TestImplementsCommand for Cp437GridCommand {}
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn round_trip_cp437_data() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										44
									
								
								src/commands/errors.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/commands/errors.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
use crate::LoadBitmapError;
 | 
			
		||||
use std::num::TryFromIntError;
 | 
			
		||||
 | 
			
		||||
/// Err values for [crate::TypedCommand::try_from].
 | 
			
		||||
#[derive(Debug, PartialEq, thiserror::Error)]
 | 
			
		||||
pub enum TryFromPacketError {
 | 
			
		||||
    /// the contained command code does not correspond to a known command
 | 
			
		||||
    #[error("The command code {0:?} does not correspond to a known command")]
 | 
			
		||||
    InvalidCommand(u16),
 | 
			
		||||
    /// the expected payload size was n, but size m was found
 | 
			
		||||
    #[error("the expected payload size was {0}, but size {1} was found")]
 | 
			
		||||
    UnexpectedPayloadSize(usize, usize),
 | 
			
		||||
    /// Header fields not needed for the command have been used.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Note that these commands would usually still work on the actual display.
 | 
			
		||||
    #[error("Header fields not needed for the command have been used")]
 | 
			
		||||
    ExtraneousHeaderValues,
 | 
			
		||||
    /// The contained compression code is not known. This could be of disabled features.
 | 
			
		||||
    #[error("The compression code {0:?} does not correspond to a known compression algorithm.")]
 | 
			
		||||
    InvalidCompressionCode(u16),
 | 
			
		||||
    /// Decompression of the payload failed. This can be caused by corrupted packets.
 | 
			
		||||
    #[error("The decompression of the payload failed")]
 | 
			
		||||
    DecompressionFailed,
 | 
			
		||||
    /// The given brightness value is out of bounds
 | 
			
		||||
    #[error("The given brightness value {0} is out of bounds.")]
 | 
			
		||||
    InvalidBrightness(u8),
 | 
			
		||||
    /// Some provided text was not valid UTF-8.
 | 
			
		||||
    #[error(transparent)]
 | 
			
		||||
    InvalidUtf8(#[from] std::string::FromUtf8Error),
 | 
			
		||||
    /// The bitmap contained in the payload could not be loaded
 | 
			
		||||
    #[error(transparent)]
 | 
			
		||||
    LoadBitmapFailed(#[from] LoadBitmapError),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// An error that can occur when parsing a raw packet as a command
 | 
			
		||||
#[derive(Debug, PartialEq, thiserror::Error)]
 | 
			
		||||
pub enum TryIntoPacketError {
 | 
			
		||||
    /// Compression of the payload failed.
 | 
			
		||||
    #[error("The compression of the payload failed")]
 | 
			
		||||
    CompressionFailed,
 | 
			
		||||
    /// Conversion (probably to u16) failed
 | 
			
		||||
    #[error(transparent)]
 | 
			
		||||
    ConversionError(#[from] TryFromIntError),
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
use crate::{
 | 
			
		||||
    command_code::CommandCode, commands::check_command_code_only,
 | 
			
		||||
    commands::TryFromPacketError, Packet, TypedCommand,
 | 
			
		||||
    commands::errors::TryFromPacketError, Packet, TypedCommand,
 | 
			
		||||
};
 | 
			
		||||
use std::fmt::Debug;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -15,7 +15,7 @@ use std::fmt::Debug;
 | 
			
		|||
/// # let connection = FakeConnection;
 | 
			
		||||
/// connection.send(FadeOutCommand).unwrap();
 | 
			
		||||
/// ```
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
			
		||||
pub struct FadeOutCommand;
 | 
			
		||||
 | 
			
		||||
impl TryFrom<Packet> for FadeOutCommand {
 | 
			
		||||
| 
						 | 
				
			
			@ -45,10 +45,11 @@ impl From<FadeOutCommand> for TypedCommand {
 | 
			
		|||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use crate::command_code::CommandCode;
 | 
			
		||||
    use crate::commands::tests::round_trip;
 | 
			
		||||
    use crate::{
 | 
			
		||||
        FadeOutCommand, Header, Packet, TryFromPacketError, TypedCommand,
 | 
			
		||||
    };
 | 
			
		||||
    use crate::commands::errors::TryFromPacketError;
 | 
			
		||||
    use crate::commands::tests::{round_trip, TestImplementsCommand};
 | 
			
		||||
    use crate::{ClearCommand, FadeOutCommand, Header, Packet, TypedCommand};
 | 
			
		||||
 | 
			
		||||
    impl TestImplementsCommand for FadeOutCommand {}
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn round_trip_fade_out() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
use crate::{
 | 
			
		||||
    command_code::CommandCode, commands::check_command_code_only,
 | 
			
		||||
    commands::TryFromPacketError, Packet, TypedCommand,
 | 
			
		||||
    commands::errors::TryFromPacketError, Packet, TypedCommand,
 | 
			
		||||
};
 | 
			
		||||
use std::fmt::Debug;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -15,7 +15,7 @@ use std::fmt::Debug;
 | 
			
		|||
/// # let connection = FakeConnection;
 | 
			
		||||
/// connection.send(HardResetCommand).unwrap();
 | 
			
		||||
/// ```
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
			
		||||
pub struct HardResetCommand;
 | 
			
		||||
 | 
			
		||||
impl TryFrom<Packet> for HardResetCommand {
 | 
			
		||||
| 
						 | 
				
			
			@ -46,9 +46,11 @@ impl From<HardResetCommand> for TypedCommand {
 | 
			
		|||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::commands::tests::round_trip;
 | 
			
		||||
    use crate::commands::tests::{round_trip, TestImplementsCommand};
 | 
			
		||||
    use crate::Header;
 | 
			
		||||
 | 
			
		||||
    impl TestImplementsCommand for HardResetCommand {}
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn round_trip_hard_reset() {
 | 
			
		||||
        round_trip(HardResetCommand.into());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,7 @@ mod brightness_grid;
 | 
			
		|||
mod char_grid;
 | 
			
		||||
mod clear;
 | 
			
		||||
mod cp437_grid;
 | 
			
		||||
mod errors;
 | 
			
		||||
mod fade_out;
 | 
			
		||||
mod hard_reset;
 | 
			
		||||
mod typed;
 | 
			
		||||
| 
						 | 
				
			
			@ -22,6 +23,7 @@ pub use brightness_grid::*;
 | 
			
		|||
pub use char_grid::*;
 | 
			
		||||
pub use clear::*;
 | 
			
		||||
pub use cp437_grid::*;
 | 
			
		||||
pub use errors::*;
 | 
			
		||||
pub use fade_out::*;
 | 
			
		||||
pub use hard_reset::*;
 | 
			
		||||
pub use typed::*;
 | 
			
		||||
| 
						 | 
				
			
			@ -74,9 +76,12 @@ pub use typed::*;
 | 
			
		|||
/// # let connection = FakeConnection;
 | 
			
		||||
/// connection.send(command).unwrap();
 | 
			
		||||
/// ```
 | 
			
		||||
pub trait Command: Debug + Clone + PartialEq + Into<Packet> {}
 | 
			
		||||
pub trait Command:
 | 
			
		||||
    Debug + Clone + Eq + TryInto<Packet> + TryFrom<Packet>
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Debug + Clone + PartialEq + Into<Packet>> Command for T {}
 | 
			
		||||
impl<T: Debug + Clone + Eq + TryInto<Packet> + TryFrom<Packet>> Command for T {}
 | 
			
		||||
 | 
			
		||||
fn check_command_code_only(
 | 
			
		||||
    packet: Packet,
 | 
			
		||||
| 
						 | 
				
			
			@ -121,8 +126,10 @@ fn check_command_code(
 | 
			
		|||
mod tests {
 | 
			
		||||
    use crate::*;
 | 
			
		||||
 | 
			
		||||
    pub(crate) trait TestImplementsCommand: Command {}
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn round_trip(original: TypedCommand) {
 | 
			
		||||
        let packet: Packet = original.clone().into();
 | 
			
		||||
        let packet: Packet = original.clone().try_into().unwrap();
 | 
			
		||||
        let copy: TypedCommand = match TypedCommand::try_from(packet) {
 | 
			
		||||
            Ok(command) => command,
 | 
			
		||||
            Err(err) => panic!("could not reload {original:?}: {err:?}"),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,70 +1,31 @@
 | 
			
		|||
use crate::{
 | 
			
		||||
    command_code::CommandCode, BitVecCommand, BitmapCommand, BrightnessCommand,
 | 
			
		||||
    BrightnessGridCommand, CharGridCommand, ClearCommand, Cp437GridCommand,
 | 
			
		||||
    FadeOutCommand, HardResetCommand, Header, LoadBitmapError, Packet,
 | 
			
		||||
    command_code::CommandCode, commands::errors::TryFromPacketError,
 | 
			
		||||
    BitVecCommand, BitmapCommand, BrightnessCommand, BrightnessGridCommand,
 | 
			
		||||
    CharGridCommand, ClearCommand, Cp437GridCommand, FadeOutCommand,
 | 
			
		||||
    HardResetCommand, Header, Packet, TryIntoPacketError,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// This enum contains all commands provided by the library.
 | 
			
		||||
/// This is useful in case you want one data type for all kinds of commands without using `dyn`.
 | 
			
		||||
///
 | 
			
		||||
/// Please look at the contained structs for documentation per command.
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
			
		||||
#[allow(missing_docs)]
 | 
			
		||||
#[allow(deprecated)]
 | 
			
		||||
pub enum TypedCommand {
 | 
			
		||||
    Clear(ClearCommand),
 | 
			
		||||
 | 
			
		||||
    CharGrid(CharGridCommand),
 | 
			
		||||
 | 
			
		||||
    Cp437Grid(Cp437GridCommand),
 | 
			
		||||
 | 
			
		||||
    Bitmap(BitmapCommand),
 | 
			
		||||
 | 
			
		||||
    Brightness(BrightnessCommand),
 | 
			
		||||
 | 
			
		||||
    BrightnessGrid(BrightnessGridCommand),
 | 
			
		||||
 | 
			
		||||
    BitVec(BitVecCommand),
 | 
			
		||||
 | 
			
		||||
    HardReset(HardResetCommand),
 | 
			
		||||
 | 
			
		||||
    FadeOut(FadeOutCommand),
 | 
			
		||||
 | 
			
		||||
    #[allow(deprecated)]
 | 
			
		||||
    #[deprecated]
 | 
			
		||||
    BitmapLegacy(crate::BitmapLegacyCommand),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Err values for [TypedCommand::try_from].
 | 
			
		||||
#[derive(Debug, PartialEq, thiserror::Error)]
 | 
			
		||||
pub enum TryFromPacketError {
 | 
			
		||||
    /// the contained command code does not correspond to a known command
 | 
			
		||||
    #[error("The command code {0:?} does not correspond to a known command")]
 | 
			
		||||
    InvalidCommand(u16),
 | 
			
		||||
    /// the expected payload size was n, but size m was found
 | 
			
		||||
    #[error("the expected payload size was {0}, but size {1} was found")]
 | 
			
		||||
    UnexpectedPayloadSize(usize, usize),
 | 
			
		||||
    /// Header fields not needed for the command have been used.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Note that these commands would usually still work on the actual display.
 | 
			
		||||
    #[error("Header fields not needed for the command have been used")]
 | 
			
		||||
    ExtraneousHeaderValues,
 | 
			
		||||
    /// The contained compression code is not known. This could be of disabled features.
 | 
			
		||||
    #[error("The compression code {0:?} does not correspond to a known compression algorithm.")]
 | 
			
		||||
    InvalidCompressionCode(u16),
 | 
			
		||||
    /// Decompression of the payload failed. This can be caused by corrupted packets.
 | 
			
		||||
    #[error("The decompression of the payload failed")]
 | 
			
		||||
    DecompressionFailed,
 | 
			
		||||
    /// The given brightness value is out of bounds
 | 
			
		||||
    #[error("The given brightness value {0} is out of bounds.")]
 | 
			
		||||
    InvalidBrightness(u8),
 | 
			
		||||
    /// Some provided text was not valid UTF-8.
 | 
			
		||||
    #[error(transparent)]
 | 
			
		||||
    InvalidUtf8(#[from] std::string::FromUtf8Error),
 | 
			
		||||
    /// The bitmap contained in the payload could not be loaded
 | 
			
		||||
    #[error(transparent)]
 | 
			
		||||
    LoadBitmapFailed(#[from] LoadBitmapError),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TryFrom<Packet> for TypedCommand {
 | 
			
		||||
    type Error = TryFromPacketError;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -136,27 +97,33 @@ impl TryFrom<Packet> for TypedCommand {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<TypedCommand> for Packet {
 | 
			
		||||
    fn from(command: TypedCommand) -> Self {
 | 
			
		||||
        match command {
 | 
			
		||||
impl TryFrom<TypedCommand> for Packet {
 | 
			
		||||
    type Error = TryIntoPacketError;
 | 
			
		||||
 | 
			
		||||
    fn try_from(value: TypedCommand) -> Result<Self, Self::Error> {
 | 
			
		||||
        Ok(match value {
 | 
			
		||||
            TypedCommand::Clear(c) => c.into(),
 | 
			
		||||
            TypedCommand::CharGrid(c) => c.into(),
 | 
			
		||||
            TypedCommand::Cp437Grid(c) => c.into(),
 | 
			
		||||
            TypedCommand::Bitmap(c) => c.into(),
 | 
			
		||||
            TypedCommand::CharGrid(c) => c.try_into()?,
 | 
			
		||||
            TypedCommand::Cp437Grid(c) => c.try_into()?,
 | 
			
		||||
            TypedCommand::Bitmap(c) => c.try_into()?,
 | 
			
		||||
            TypedCommand::Brightness(c) => c.into(),
 | 
			
		||||
            TypedCommand::BrightnessGrid(c) => c.into(),
 | 
			
		||||
            TypedCommand::BitVec(c) => c.into(),
 | 
			
		||||
            TypedCommand::BrightnessGrid(c) => c.try_into()?,
 | 
			
		||||
            TypedCommand::BitVec(c) => c.try_into()?,
 | 
			
		||||
            TypedCommand::HardReset(c) => c.into(),
 | 
			
		||||
            TypedCommand::FadeOut(c) => c.into(),
 | 
			
		||||
            #[allow(deprecated)]
 | 
			
		||||
            TypedCommand::BitmapLegacy(c) => c.into(),
 | 
			
		||||
        }
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use crate::{Header, Packet, TryFromPacketError, TypedCommand};
 | 
			
		||||
    use crate::commands::errors::TryFromPacketError;
 | 
			
		||||
    use crate::commands::tests::TestImplementsCommand;
 | 
			
		||||
    use crate::{Header, Packet, TypedCommand};
 | 
			
		||||
 | 
			
		||||
    impl TestImplementsCommand for TypedCommand {}
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn error_invalid_command() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,6 +5,7 @@ use std::io::{Read, Write};
 | 
			
		|||
use bzip2::read::{BzDecoder, BzEncoder};
 | 
			
		||||
#[cfg(feature = "compression_zlib")]
 | 
			
		||||
use flate2::{FlushCompress, FlushDecompress, Status};
 | 
			
		||||
use log::error;
 | 
			
		||||
#[cfg(feature = "compression_zstd")]
 | 
			
		||||
use zstd::{Decoder as ZstdDecoder, Encoder as ZstdEncoder};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -67,28 +68,39 @@ pub(crate) fn into_decompressed(
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[allow(clippy::unwrap_used)]
 | 
			
		||||
pub(crate) fn into_compressed(
 | 
			
		||||
    kind: CompressionCode,
 | 
			
		||||
    payload: Payload,
 | 
			
		||||
) -> Payload {
 | 
			
		||||
) -> Option<Payload> {
 | 
			
		||||
    match kind {
 | 
			
		||||
        CompressionCode::Uncompressed => payload,
 | 
			
		||||
        CompressionCode::Uncompressed => Some(payload),
 | 
			
		||||
        #[cfg(feature = "compression_zlib")]
 | 
			
		||||
        CompressionCode::Zlib => {
 | 
			
		||||
            let mut compress =
 | 
			
		||||
                flate2::Compress::new(flate2::Compression::fast(), true);
 | 
			
		||||
            let mut buffer = [0u8; 10000];
 | 
			
		||||
 | 
			
		||||
            match compress
 | 
			
		||||
                .compress(&payload, &mut buffer, FlushCompress::Finish)
 | 
			
		||||
                .expect("compress failed")
 | 
			
		||||
            {
 | 
			
		||||
                Status::Ok => panic!("buffer should be big enough"),
 | 
			
		||||
                Status::BufError => panic!("BufError"),
 | 
			
		||||
                Status::StreamEnd => {}
 | 
			
		||||
            };
 | 
			
		||||
            buffer[..compress.total_out() as usize].to_owned()
 | 
			
		||||
            match compress.compress(
 | 
			
		||||
                &payload,
 | 
			
		||||
                &mut buffer,
 | 
			
		||||
                FlushCompress::Finish,
 | 
			
		||||
            ) {
 | 
			
		||||
                Ok(Status::Ok) => {
 | 
			
		||||
                    error!("buffer not big enough");
 | 
			
		||||
                    None
 | 
			
		||||
                }
 | 
			
		||||
                Ok(Status::BufError) => {
 | 
			
		||||
                    error!("Could not compress: {:?}", Status::BufError);
 | 
			
		||||
                    None
 | 
			
		||||
                }
 | 
			
		||||
                Ok(Status::StreamEnd) => {
 | 
			
		||||
                    Some(buffer[..compress.total_out() as usize].to_owned())
 | 
			
		||||
                }
 | 
			
		||||
                Err(_) => {
 | 
			
		||||
                    error!("compress returned err");
 | 
			
		||||
                    None
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        #[cfg(feature = "compression_bzip2")]
 | 
			
		||||
        CompressionCode::Bzip2 => {
 | 
			
		||||
| 
						 | 
				
			
			@ -96,21 +108,39 @@ pub(crate) fn into_compressed(
 | 
			
		|||
                BzEncoder::new(&*payload, bzip2::Compression::fast());
 | 
			
		||||
            let mut compressed = vec![];
 | 
			
		||||
            match encoder.read_to_end(&mut compressed) {
 | 
			
		||||
                Err(err) => panic!("could not compress payload: {}", err),
 | 
			
		||||
                Ok(_) => compressed,
 | 
			
		||||
                Err(err) => {
 | 
			
		||||
                    error!("Could not compress: {:?}", err);
 | 
			
		||||
                    None
 | 
			
		||||
                }
 | 
			
		||||
                Ok(_) => Some(compressed),
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        #[cfg(feature = "compression_lzma")]
 | 
			
		||||
        CompressionCode::Lzma => lzma::compress(&payload, 6).unwrap(),
 | 
			
		||||
        CompressionCode::Lzma => match lzma::compress(&payload, 6) {
 | 
			
		||||
            Ok(payload) => Some(payload),
 | 
			
		||||
            Err(e) => {
 | 
			
		||||
                error!("Could not compress: {e:?}");
 | 
			
		||||
                None
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        #[cfg(feature = "compression_zstd")]
 | 
			
		||||
        CompressionCode::Zstd => {
 | 
			
		||||
            let buf = Vec::with_capacity(payload.len());
 | 
			
		||||
            let mut encoder =
 | 
			
		||||
                ZstdEncoder::new(vec![], zstd::DEFAULT_COMPRESSION_LEVEL)
 | 
			
		||||
                    .expect("could not create encoder");
 | 
			
		||||
            encoder
 | 
			
		||||
                .write_all(&payload)
 | 
			
		||||
                .expect("could not compress payload");
 | 
			
		||||
            encoder.finish().expect("could not finish encoding")
 | 
			
		||||
                match ZstdEncoder::new(buf, zstd::DEFAULT_COMPRESSION_LEVEL) {
 | 
			
		||||
                    Err(e) => {
 | 
			
		||||
                        error!("failed to create decoder: {e:?}");
 | 
			
		||||
                        return None;
 | 
			
		||||
                    }
 | 
			
		||||
                    Ok(encoder) => encoder,
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
            if let Err(e) = encoder.write_all(&payload) {
 | 
			
		||||
                error!("failed to decompress payload: {e:?}");
 | 
			
		||||
                return None;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            encoder.finish().ok()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,7 +21,7 @@
 | 
			
		|||
/// };
 | 
			
		||||
/// ```
 | 
			
		||||
#[repr(u16)]
 | 
			
		||||
#[derive(Debug, Clone, Copy, PartialEq)]
 | 
			
		||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 | 
			
		||||
pub enum CompressionCode {
 | 
			
		||||
    /// no compression
 | 
			
		||||
    Uncompressed = 0x0,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,16 +1,28 @@
 | 
			
		|||
use crate::{Connection, Packet};
 | 
			
		||||
use crate::{Connection, Packet, SendError};
 | 
			
		||||
use log::debug;
 | 
			
		||||
use std::{convert::Infallible, error::Error, fmt::Debug};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
/// A fake connection for testing that does not actually send anything.
 | 
			
		||||
pub struct FakeConnection;
 | 
			
		||||
 | 
			
		||||
impl Connection for FakeConnection {
 | 
			
		||||
    // TODO: () does not implement Error+Debug, some placeholder is needed
 | 
			
		||||
    type Error = std::io::Error;
 | 
			
		||||
    type TransportError = Infallible;
 | 
			
		||||
 | 
			
		||||
    fn send(&self, packet: impl Into<Packet>) -> Result<(), Self::Error> {
 | 
			
		||||
        let data: Vec<u8> = packet.into().into();
 | 
			
		||||
    fn send<P: TryInto<Packet>>(
 | 
			
		||||
        &self,
 | 
			
		||||
        packet: P,
 | 
			
		||||
    ) -> Result<
 | 
			
		||||
        (),
 | 
			
		||||
        SendError<<P as TryInto<Packet>>::Error, Self::TransportError>,
 | 
			
		||||
    >
 | 
			
		||||
    where
 | 
			
		||||
        <P as TryInto<Packet>>::Error: Error + Debug,
 | 
			
		||||
    {
 | 
			
		||||
        let data: Vec<u8> = packet
 | 
			
		||||
            .try_into()
 | 
			
		||||
            .map(Into::<Vec<u8>>::into)
 | 
			
		||||
            .map_err(SendError::IntoPacket)?;
 | 
			
		||||
        debug!("Sending fake packet: {data:?}");
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,7 @@
 | 
			
		|||
//! This module contains the [Connection] trait and all implementations provided in this library.
 | 
			
		||||
 | 
			
		||||
use crate::Packet;
 | 
			
		||||
use std::error::Error;
 | 
			
		||||
use std::fmt::Debug;
 | 
			
		||||
use std::{error::Error, fmt::Debug};
 | 
			
		||||
 | 
			
		||||
mod fake;
 | 
			
		||||
#[cfg(feature = "protocol_udp")]
 | 
			
		||||
| 
						 | 
				
			
			@ -16,6 +15,20 @@ pub use udp::*;
 | 
			
		|||
#[cfg(feature = "protocol_websocket")]
 | 
			
		||||
pub use websocket::*;
 | 
			
		||||
 | 
			
		||||
/// An error that can happen when sending a command
 | 
			
		||||
#[derive(Debug, thiserror::Error)]
 | 
			
		||||
pub enum SendError<
 | 
			
		||||
    IntoPacketError: Error + Debug,
 | 
			
		||||
    TransportError: Error + Debug,
 | 
			
		||||
> {
 | 
			
		||||
    /// An error occurred while sending the bytes via the underlying transport
 | 
			
		||||
    #[error("An error occurred while sending the bytes via the underlying transport: {0:?}")]
 | 
			
		||||
    Transport(TransportError),
 | 
			
		||||
    /// An error occurred while preparing the data to send
 | 
			
		||||
    #[error("An error occurred while preparing the data to send: {0:?}")]
 | 
			
		||||
    IntoPacket(IntoPacketError),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A connection to the display.
 | 
			
		||||
///
 | 
			
		||||
/// Used to send [Packets][Packet] or [Commands][crate::Command].
 | 
			
		||||
| 
						 | 
				
			
			@ -30,7 +43,7 @@ pub use websocket::*;
 | 
			
		|||
/// ```
 | 
			
		||||
pub trait Connection: Debug {
 | 
			
		||||
    /// The error that can occur when sending a packet
 | 
			
		||||
    type Error: Error + Debug;
 | 
			
		||||
    type TransportError: Error + Debug;
 | 
			
		||||
 | 
			
		||||
    /// Send something packet-like to the display. Usually this is in the form of a Command.
 | 
			
		||||
    ///
 | 
			
		||||
| 
						 | 
				
			
			@ -49,5 +62,13 @@ pub trait Connection: Debug {
 | 
			
		|||
    ///  connection.send(servicepoint::ClearCommand)
 | 
			
		||||
    ///      .expect("send failed");
 | 
			
		||||
    /// ```
 | 
			
		||||
    fn send(&self, packet: impl Into<Packet>) -> Result<(), Self::Error>;
 | 
			
		||||
    fn send<P: TryInto<Packet>>(
 | 
			
		||||
        &self,
 | 
			
		||||
        packet: P,
 | 
			
		||||
    ) -> Result<
 | 
			
		||||
        (),
 | 
			
		||||
        SendError<<P as TryInto<Packet>>::Error, Self::TransportError>,
 | 
			
		||||
    >
 | 
			
		||||
    where
 | 
			
		||||
        <P as TryInto<Packet>>::Error: Error + Debug;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,5 @@
 | 
			
		|||
use crate::{Connection, Packet};
 | 
			
		||||
use std::fmt::Debug;
 | 
			
		||||
use std::net::UdpSocket;
 | 
			
		||||
use crate::{Connection, Packet, SendError};
 | 
			
		||||
use std::{error::Error, fmt::Debug, net::UdpSocket};
 | 
			
		||||
 | 
			
		||||
/// A connection using the UDP protocol.
 | 
			
		||||
///
 | 
			
		||||
| 
						 | 
				
			
			@ -39,11 +38,26 @@ impl UdpConnection {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
impl Connection for UdpConnection {
 | 
			
		||||
    type Error = std::io::Error;
 | 
			
		||||
    type TransportError = std::io::Error;
 | 
			
		||||
 | 
			
		||||
    fn send(&self, packet: impl Into<Packet>) -> Result<(), Self::Error> {
 | 
			
		||||
        let data: Vec<u8> = packet.into().into();
 | 
			
		||||
        self.socket.send(&data).map(move |_| ()) // ignore Ok value
 | 
			
		||||
    fn send<P: TryInto<Packet>>(
 | 
			
		||||
        &self,
 | 
			
		||||
        packet: P,
 | 
			
		||||
    ) -> Result<
 | 
			
		||||
        (),
 | 
			
		||||
        SendError<<P as TryInto<Packet>>::Error, Self::TransportError>,
 | 
			
		||||
    >
 | 
			
		||||
    where
 | 
			
		||||
        <P as TryInto<Packet>>::Error: Error + Debug,
 | 
			
		||||
    {
 | 
			
		||||
        let data: Vec<u8> = packet
 | 
			
		||||
            .try_into()
 | 
			
		||||
            .map(Into::<Vec<u8>>::into)
 | 
			
		||||
            .map_err(SendError::IntoPacket)?;
 | 
			
		||||
        self.socket
 | 
			
		||||
            .send(&data)
 | 
			
		||||
            .map(move |_| ())
 | 
			
		||||
            .map_err(SendError::Transport) // ignore Ok value
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
use crate::{Connection, Packet};
 | 
			
		||||
use crate::{Connection, Packet, SendError};
 | 
			
		||||
use std::{error::Error, fmt::Debug};
 | 
			
		||||
 | 
			
		||||
/// A connection using the WebSocket protocol.
 | 
			
		||||
///
 | 
			
		||||
| 
						 | 
				
			
			@ -20,12 +21,27 @@ pub struct WebsocketConnection(
 | 
			
		|||
);
 | 
			
		||||
 | 
			
		||||
impl Connection for WebsocketConnection {
 | 
			
		||||
    type Error = tungstenite::Error;
 | 
			
		||||
    type TransportError = tungstenite::Error;
 | 
			
		||||
 | 
			
		||||
    fn send(&self, packet: impl Into<Packet>) -> Result<(), Self::Error> {
 | 
			
		||||
        let data: Vec<u8> = packet.into().into();
 | 
			
		||||
    fn send<P: TryInto<Packet>>(
 | 
			
		||||
        &self,
 | 
			
		||||
        packet: P,
 | 
			
		||||
    ) -> Result<
 | 
			
		||||
        (),
 | 
			
		||||
        SendError<<P as TryInto<Packet>>::Error, Self::TransportError>,
 | 
			
		||||
    >
 | 
			
		||||
    where
 | 
			
		||||
        <P as TryInto<Packet>>::Error: Error + Debug,
 | 
			
		||||
    {
 | 
			
		||||
        let data: Vec<u8> = packet
 | 
			
		||||
            .try_into()
 | 
			
		||||
            .map(Into::<Vec<u8>>::into)
 | 
			
		||||
            .map_err(SendError::IntoPacket)?
 | 
			
		||||
            .into();
 | 
			
		||||
        let mut socket = self.0.lock().unwrap();
 | 
			
		||||
        socket.send(tungstenite::Message::Binary(data.into()))
 | 
			
		||||
        socket
 | 
			
		||||
            .send(tungstenite::Message::Binary(data.into()))
 | 
			
		||||
            .map_err(SendError::Transport)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -63,6 +79,6 @@ impl WebsocketConnection {
 | 
			
		|||
 | 
			
		||||
impl Drop for WebsocketConnection {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        _ = self.0.try_lock().map(move |mut sock| sock.close(None));
 | 
			
		||||
        drop(self.0.try_lock().map(move |mut sock| sock.close(None)));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -153,6 +153,7 @@ impl Bitmap {
 | 
			
		|||
    ///     pixel.set(index % 2 == 0)
 | 
			
		||||
    /// }
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn iter_mut(&mut self) -> IterMut<u8, Msb0> {
 | 
			
		||||
        self.bit_vec.iter_mut()
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -258,6 +259,7 @@ impl From<&Bitmap> for ValueGrid<bool> {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[must_use]
 | 
			
		||||
struct IterRows<'t> {
 | 
			
		||||
    bitmap: &'t Bitmap,
 | 
			
		||||
    row: usize,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,6 +33,7 @@ impl CharGrid {
 | 
			
		|||
    /// # use servicepoint::CharGrid;
 | 
			
		||||
    /// let grid = CharGrid::wrap_str(2, "abc\ndef");
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn wrap_str(width: usize, text: &str) -> Self {
 | 
			
		||||
        let lines = text
 | 
			
		||||
            .split('\n')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,7 +13,7 @@ impl<T: Sized + Default + Copy + Clone + Debug> Value for T {}
 | 
			
		|||
///
 | 
			
		||||
/// This structure can be used with any type that implements the [Value] trait.
 | 
			
		||||
/// You can also use the concrete type aliases provided in this crate, e.g. [CharGrid] and [ByteGrid].
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
			
		||||
pub struct ValueGrid<T: Value> {
 | 
			
		||||
    width: usize,
 | 
			
		||||
    height: usize,
 | 
			
		||||
| 
						 | 
				
			
			@ -50,6 +50,7 @@ impl<T: Value> ValueGrid<T> {
 | 
			
		|||
    /// - height: size in y-direction
 | 
			
		||||
    ///
 | 
			
		||||
    /// returns: [ValueGrid] initialized to default value.
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn new(width: usize, height: usize) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            data: vec![Default::default(); width * height],
 | 
			
		||||
| 
						 | 
				
			
			@ -193,6 +194,7 @@ impl<T: Value> ValueGrid<T> {
 | 
			
		|||
    /// ```
 | 
			
		||||
    /// [Brightness]: [crate::Brightness]
 | 
			
		||||
    /// [Command]: [crate::Command]
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn map<TConverted, F>(&self, f: F) -> ValueGrid<TConverted>
 | 
			
		||||
    where
 | 
			
		||||
        TConverted: Value,
 | 
			
		||||
| 
						 | 
				
			
			@ -358,11 +360,13 @@ impl<T: Value> Grid<T> for ValueGrid<T> {
 | 
			
		|||
 | 
			
		||||
impl<T: Value> DataRef<T> for ValueGrid<T> {
 | 
			
		||||
    /// Get the underlying byte rows mutable
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    fn data_ref_mut(&mut self) -> &mut [T] {
 | 
			
		||||
        self.data.as_mut_slice()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the underlying byte rows read only
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    fn data_ref(&self) -> &[T] {
 | 
			
		||||
        self.data.as_slice()
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -376,6 +380,7 @@ impl<T: Value> From<ValueGrid<T>> for Vec<T> {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
/// An iterator iver the rows in a [ValueGrid]
 | 
			
		||||
#[must_use]
 | 
			
		||||
pub struct IterGridRows<'t, T: Value> {
 | 
			
		||||
    grid: &'t ValueGrid<T>,
 | 
			
		||||
    row: usize,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@ use crate::TILE_SIZE;
 | 
			
		|||
use std::marker::PhantomData;
 | 
			
		||||
 | 
			
		||||
/// An origin marks the top left position of a window sent to the display.
 | 
			
		||||
#[derive(Debug, Copy, Clone, PartialEq)]
 | 
			
		||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
 | 
			
		||||
pub struct Origin<Unit: DisplayUnit> {
 | 
			
		||||
    /// position in the width direction
 | 
			
		||||
    pub x: usize,
 | 
			
		||||
| 
						 | 
				
			
			@ -44,11 +44,11 @@ impl<T: DisplayUnit> std::ops::Add<Origin<T>> for Origin<T> {
 | 
			
		|||
pub trait DisplayUnit {}
 | 
			
		||||
 | 
			
		||||
/// Marks something to be measured in number of pixels.
 | 
			
		||||
#[derive(Debug, Copy, Clone, PartialEq)]
 | 
			
		||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
 | 
			
		||||
pub struct Pixels();
 | 
			
		||||
 | 
			
		||||
/// Marks something to be measured in number of iles.
 | 
			
		||||
#[derive(Debug, Copy, Clone, PartialEq)]
 | 
			
		||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
 | 
			
		||||
pub struct Tiles();
 | 
			
		||||
 | 
			
		||||
impl DisplayUnit for Pixels {}
 | 
			
		||||
| 
						 | 
				
			
			@ -86,6 +86,12 @@ impl TryFrom<&Origin<Pixels>> for Origin<Tiles> {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<Unit: DisplayUnit> Default for Origin<Unit> {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self::ZERO
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,9 +23,8 @@
 | 
			
		|||
//! let packet = Packet::try_from(bytes).expect("could not read packet from bytes");
 | 
			
		||||
//! ```
 | 
			
		||||
 | 
			
		||||
use crate::command_code::CommandCode;
 | 
			
		||||
use crate::{Grid, Origin, Tiles};
 | 
			
		||||
use std::mem::size_of;
 | 
			
		||||
use crate::{command_code::CommandCode, Grid, Origin, Tiles};
 | 
			
		||||
use std::{mem::size_of, num::TryFromIntError};
 | 
			
		||||
 | 
			
		||||
/// A raw header.
 | 
			
		||||
///
 | 
			
		||||
| 
						 | 
				
			
			@ -144,17 +143,17 @@ impl Packet {
 | 
			
		|||
        origin: Origin<Tiles>,
 | 
			
		||||
        grid: impl Grid<T> + Into<Payload>,
 | 
			
		||||
        command_code: CommandCode,
 | 
			
		||||
    ) -> Packet {
 | 
			
		||||
        Packet {
 | 
			
		||||
    ) -> Result<Packet, TryFromIntError> {
 | 
			
		||||
        Ok(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,
 | 
			
		||||
                a: origin.x.try_into()?,
 | 
			
		||||
                b: origin.y.try_into()?,
 | 
			
		||||
                c: grid.width().try_into()?,
 | 
			
		||||
                d: grid.height().try_into()?,
 | 
			
		||||
            },
 | 
			
		||||
            payload: grid.into(),
 | 
			
		||||
        }
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn command_code_only(c: CommandCode) -> Self {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue