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