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