From d0598446e60b0c4f3f0de1063c20d1866c742af3 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Tue, 12 Nov 2024 19:32:42 +0100 Subject: [PATCH 01/18] make BitVec type alias pub --- crates/servicepoint/src/bitmap.rs | 4 ++-- crates/servicepoint/src/command.rs | 14 ++++++-------- crates/servicepoint/src/lib.rs | 4 ++-- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/crates/servicepoint/src/bitmap.rs b/crates/servicepoint/src/bitmap.rs index 657e155..d7d7a77 100644 --- a/crates/servicepoint/src/bitmap.rs +++ b/crates/servicepoint/src/bitmap.rs @@ -2,14 +2,14 @@ use bitvec::order::Msb0; use bitvec::prelude::BitSlice; use bitvec::slice::IterMut; -use crate::{BitVec, DataRef, Grid, SpBitVec, PIXEL_HEIGHT, PIXEL_WIDTH}; +use crate::{BitVec, DataRef, Grid, PIXEL_HEIGHT, PIXEL_WIDTH}; /// A grid of pixels stored in packed bytes. #[derive(Debug, Clone, PartialEq)] pub struct Bitmap { width: usize, height: usize, - bit_vec: SpBitVec, + bit_vec: BitVec, } impl Bitmap { diff --git a/crates/servicepoint/src/command.rs b/crates/servicepoint/src/command.rs index eead79d..8d42530 100644 --- a/crates/servicepoint/src/command.rs +++ b/crates/servicepoint/src/command.rs @@ -1,11 +1,9 @@ -use bitvec::prelude::BitVec; - use crate::{ command_code::CommandCode, compression::into_decompressed, packet::{Header, Packet}, Bitmap, Brightness, BrightnessGrid, CompressionCode, Cp437Grid, Origin, - Pixels, PrimitiveGrid, SpBitVec, Tiles, TILE_SIZE, + Pixels, PrimitiveGrid, BitVec, Tiles, TILE_SIZE, }; /// Type alias for documenting the meaning of the u16 in enum values @@ -144,7 +142,7 @@ pub enum Command { /// once the starting row is full, overwriting will continue on column 0. /// /// The contained [BitVec] is always uncompressed. - BitmapLinear(Offset, SpBitVec, CompressionCode), + BitmapLinear(Offset, BitVec, CompressionCode), /// Set pixel data according to an and-mask starting at the offset. /// @@ -152,7 +150,7 @@ pub enum Command { /// once the starting row is full, overwriting will continue on column 0. /// /// The contained [BitVec] is always uncompressed. - BitmapLinearAnd(Offset, SpBitVec, CompressionCode), + BitmapLinearAnd(Offset, BitVec, CompressionCode), /// Set pixel data according to an or-mask starting at the offset. /// @@ -160,7 +158,7 @@ pub enum Command { /// once the starting row is full, overwriting will continue on column 0. /// /// The contained [BitVec] is always uncompressed. - BitmapLinearOr(Offset, SpBitVec, CompressionCode), + BitmapLinearOr(Offset, BitVec, CompressionCode), /// Set pixel data according to a xor-mask starting at the offset. /// @@ -168,7 +166,7 @@ pub enum Command { /// once the starting row is full, overwriting will continue on column 0. /// /// The contained [BitVec] is always uncompressed. - BitmapLinearXor(Offset, SpBitVec, CompressionCode), + BitmapLinearXor(Offset, BitVec, CompressionCode), /// Kills the udp daemon on the display, which usually results in a restart. /// @@ -374,7 +372,7 @@ impl Command { /// Helper method for Packets into `BitmapLinear*`-Commands fn packet_into_linear_bitmap( packet: Packet, - ) -> Result<(SpBitVec, CompressionCode), TryFromPacketError> { + ) -> Result<(BitVec, CompressionCode), TryFromPacketError> { let Packet { header: Header { diff --git a/crates/servicepoint/src/lib.rs b/crates/servicepoint/src/lib.rs index 39b4077..3c1fc02 100644 --- a/crates/servicepoint/src/lib.rs +++ b/crates/servicepoint/src/lib.rs @@ -38,7 +38,6 @@ use std::time::Duration; pub use bitvec; -use bitvec::prelude::{BitVec, Msb0}; pub use crate::bitmap::Bitmap; pub use crate::brightness::{Brightness, BrightnessGrid}; @@ -51,7 +50,8 @@ pub use crate::grid::Grid; pub use crate::origin::{Origin, Pixels, Tiles}; pub use crate::primitive_grid::PrimitiveGrid; -type SpBitVec = BitVec; +/// An alias for the specific type of [bitvec::prelude::BitVec] used. +pub type BitVec = bitvec::prelude::BitVec; mod bitmap; mod brightness; From 7bc59f6df8106ed23aada1737ab6ba8261bcb6ca Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Tue, 12 Nov 2024 19:33:54 +0100 Subject: [PATCH 02/18] add BrightnessGrid::saturating_load --- crates/servicepoint/src/brightness.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/crates/servicepoint/src/brightness.rs b/crates/servicepoint/src/brightness.rs index e5a82b3..009b8fe 100644 --- a/crates/servicepoint/src/brightness.rs +++ b/crates/servicepoint/src/brightness.rs @@ -37,6 +37,13 @@ pub struct Brightness(u8); /// ``` pub type BrightnessGrid = PrimitiveGrid; +impl BrightnessGrid { + /// Like [Self::load], but ignoring any out-of-range brightness values + pub fn saturating_load(width: usize, height: usize, data: &[u8]) -> Self { + PrimitiveGrid::load(width, height, data).map(Brightness::saturating_from) + } +} + impl From for u8 { fn from(brightness: Brightness) -> Self { brightness.0 @@ -105,7 +112,7 @@ impl TryFrom> for BrightnessGrid { let brightnesses = value .iter() .map(|b| Brightness::try_from(*b)) - .collect::, _>>()?; + .collect::, _>>()?; Ok(BrightnessGrid::load( value.width(), value.height(), @@ -155,4 +162,10 @@ mod tests { assert_eq!(Brightness::MAX, Brightness::saturating_from(100)); assert_eq!(Brightness(5), Brightness::saturating_from(5)); } + + #[test] + fn saturating_load() { + assert_eq!(BrightnessGrid::load(2,2, &[Brightness::MAX, Brightness::MAX, Brightness::MIN, Brightness::MAX]), + BrightnessGrid::saturating_load(2,2, &[255u8, 23, 0, 42])); + } } From 4f1ef8aafb00a3b2f9330d6235caaaa31526a8df Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Tue, 12 Nov 2024 19:34:50 +0100 Subject: [PATCH 03/18] replace Connection::send_mut with internal mutability --- crates/servicepoint/examples/websocket.rs | 9 +-- crates/servicepoint/src/connection.rs | 93 +++-------------------- 2 files changed, 15 insertions(+), 87 deletions(-) diff --git a/crates/servicepoint/examples/websocket.rs b/crates/servicepoint/examples/websocket.rs index a0c43c0..207609e 100644 --- a/crates/servicepoint/examples/websocket.rs +++ b/crates/servicepoint/examples/websocket.rs @@ -5,20 +5,17 @@ use servicepoint::{ }; fn main() { - // make connection mut - let mut connection = + let connection = Connection::open_websocket("ws://localhost:8080".parse().unwrap()) .unwrap(); - // use send_mut instead of send - connection.send_mut(Command::Clear).unwrap(); + connection.send(Command::Clear).unwrap(); let mut pixels = Bitmap::max_sized(); pixels.fill(true); - // use send_mut instead of send connection - .send_mut(Command::BitmapLinearWin( + .send(Command::BitmapLinearWin( Origin::ZERO, pixels, CompressionCode::Lzma, diff --git a/crates/servicepoint/src/connection.rs b/crates/servicepoint/src/connection.rs index 5d4e902..721583c 100644 --- a/crates/servicepoint/src/connection.rs +++ b/crates/servicepoint/src/connection.rs @@ -1,5 +1,4 @@ use std::fmt::Debug; - use crate::packet::Packet; /// A connection to the display. @@ -35,20 +34,13 @@ pub enum Connection { /// [servicepoint-websocket-relay]: https://github.com/kaesaecracker/servicepoint-websocket-relay #[cfg(feature = "protocol_websocket")] WebSocket( - tungstenite::WebSocket< + std::sync::Arc, - >, + >>>, ), /// A fake connection for testing that does not actually send anything. - /// - /// This variant allows immutable send. Fake, - - /// A fake connection for testing that does not actually send anything. - /// - /// This variant does not allow immutable send. - FakeMutableSend, } #[derive(Debug)] @@ -96,7 +88,7 @@ impl Connection { /// let uri = "ws://localhost:8080".parse().unwrap(); /// let mut connection = Connection::open_websocket(uri) /// .expect("could not connect"); - /// connection.send_mut(Command::Clear) + /// connection.send(Command::Clear) /// .expect("send failed"); /// ``` #[cfg(feature = "protocol_websocket")] @@ -111,25 +103,17 @@ impl Connection { let request = ClientRequestBuilder::new(uri).into_client_request()?; let (sock, _) = connect(request)?; - Ok(Self::WebSocket(sock)) + Ok(Self::WebSocket(std::sync::Arc::new(std::sync::Mutex::new(sock)))) } /// Send something packet-like to the display. Usually this is in the form of a Command. /// - /// This variant can only be used for connections that support immutable send, e.g. [Connection::Udp]. - /// - /// If you want to be able to switch the protocol, you should use [Self::send_mut] instead. - /// /// # Arguments /// /// - `packet`: the packet-like to send /// /// returns: true if packet was sent, otherwise false /// - /// # Panics - /// - /// If the connection does not support immutable send, e.g. for [Connection::WebSocket]. - /// /// # Examples /// /// ```rust @@ -150,6 +134,13 @@ impl Connection { .map_err(SendError::IoError) .map(move |_| ()) // ignore Ok value } + #[cfg(feature = "protocol_websocket")] + Connection::WebSocket(socket) => { + let mut socket = socket.lock().unwrap(); + socket + .send(tungstenite::Message::Binary(data)) + .map_err(SendError::WebsocketError) + } Connection::Fake => { let _ = data; Ok(()) @@ -160,58 +151,13 @@ impl Connection { } } } - - /// Send something packet-like to the display. Usually this is in the form of a Command. - /// - /// This variant has to be used for connections that do not support immutable send, e.g. [Connection::WebSocket]. - /// - /// If you want to be able to switch the protocol, you should use this variant. - /// - /// # Arguments - /// - /// - `packet`: the packet-like to send - /// - /// returns: true if packet was sent, otherwise false - /// - /// # Examples - /// - /// ```rust - /// let mut connection = servicepoint::Connection::FakeMutableSend; - /// // turn off all pixels on display - /// connection.send_mut(servicepoint::Command::Clear) - /// .expect("send failed"); - /// ``` - pub fn send_mut( - &mut self, - packet: impl Into, - ) -> Result<(), SendError> { - match self { - #[cfg(feature = "protocol_websocket")] - Connection::WebSocket(socket) => { - let packet = packet.into(); - log::debug!("sending {packet:?}"); - let data: Vec = packet.into(); - socket - .send(tungstenite::Message::Binary(data)) - .map_err(SendError::WebsocketError) - } - Connection::FakeMutableSend => { - let packet = packet.into(); - log::debug!("sending {packet:?}"); - let data: Vec = packet.into(); - let _ = data; - Ok(()) - } - _ => self.send(packet), - } - } } impl Drop for Connection { fn drop(&mut self) { #[cfg(feature = "protocol_websocket")] if let Connection::WebSocket(sock) = self { - _ = sock.close(None); + _ = sock.try_lock().map(move |mut sock| sock.close(None).unwrap() ); } } } @@ -227,19 +173,4 @@ mod tests { let packet = Packet::try_from(data).unwrap(); Connection::Fake.send(packet).unwrap() } - - #[test] - fn send_fake_mutable() { - let data: &[u8] = &[0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - let packet = Packet::try_from(data).unwrap(); - Connection::FakeMutableSend.send_mut(packet).unwrap() - } - - #[test] - #[should_panic] - fn send_fake_mutable_panic() { - let data: &[u8] = &[0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - let packet = Packet::try_from(data).unwrap(); - Connection::FakeMutableSend.send(packet).unwrap() - } } From eb42e0aba1527643806d875740509aca4aa6edcc Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Tue, 12 Nov 2024 19:36:12 +0100 Subject: [PATCH 04/18] add derives, u8::from(&Brightness) --- crates/servicepoint/src/bitmap.rs | 2 +- crates/servicepoint/src/brightness.rs | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/servicepoint/src/bitmap.rs b/crates/servicepoint/src/bitmap.rs index d7d7a77..697a0d5 100644 --- a/crates/servicepoint/src/bitmap.rs +++ b/crates/servicepoint/src/bitmap.rs @@ -5,7 +5,7 @@ use bitvec::slice::IterMut; use crate::{BitVec, DataRef, Grid, PIXEL_HEIGHT, PIXEL_WIDTH}; /// A grid of pixels stored in packed bytes. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Bitmap { width: usize, height: usize, diff --git a/crates/servicepoint/src/brightness.rs b/crates/servicepoint/src/brightness.rs index 009b8fe..29c5576 100644 --- a/crates/servicepoint/src/brightness.rs +++ b/crates/servicepoint/src/brightness.rs @@ -19,7 +19,7 @@ use rand::{ /// # let connection = Connection::open("127.0.0.1:2342").unwrap(); /// let result = connection.send(Command::Brightness(b)); /// ``` -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)] pub struct Brightness(u8); /// A grid containing brightness values. @@ -50,6 +50,12 @@ impl From for u8 { } } +impl From<&Brightness> for u8 { + fn from(brightness: &Brightness) -> Self { + brightness.0 + } +} + impl TryFrom for Brightness { type Error = u8; From 85a0ea0dc38d52fde418ba5e32bfc117816055aa Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sun, 10 Nov 2024 17:43:41 +0100 Subject: [PATCH 05/18] add row and column ops for primitive grid --- crates/servicepoint/src/brightness.rs | 4 +- crates/servicepoint/src/primitive_grid.rs | 82 +++++++++++++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/crates/servicepoint/src/brightness.rs b/crates/servicepoint/src/brightness.rs index 29c5576..7787cc2 100644 --- a/crates/servicepoint/src/brightness.rs +++ b/crates/servicepoint/src/brightness.rs @@ -46,7 +46,7 @@ impl BrightnessGrid { impl From for u8 { fn from(brightness: Brightness) -> Self { - brightness.0 + Self::from(&brightness) } } @@ -142,7 +142,7 @@ mod tests { #[test] fn brightness_from_u8() { assert_eq!(Err(100), Brightness::try_from(100)); - assert_eq!(Ok(Brightness(1)), Brightness::try_from(1)) + assert_eq!(Ok(Brightness(1)), Brightness::try_from(1)); } #[test] diff --git a/crates/servicepoint/src/primitive_grid.rs b/crates/servicepoint/src/primitive_grid.rs index 69dcf0f..72bcfc0 100644 --- a/crates/servicepoint/src/primitive_grid.rs +++ b/crates/servicepoint/src/primitive_grid.rs @@ -138,6 +138,64 @@ impl PrimitiveGrid { .collect::>(); PrimitiveGrid::load(self.width(), self.height(), &data) } + + /// Copies a row from the grid. + /// + /// Returns [None] if y is out of bounds. + pub fn get_row(&self, y: usize) -> Option> { + self.data + .chunks_exact(self.width()) + .nth(y) + .map(|row| row.to_vec()) + } + + /// Copies a column from the grid. + /// + /// Returns [None] if x is out of bounds. + pub fn get_col(&self, x: usize) -> Option> { + self.data + .chunks_exact(self.width()) + .map(|row| row.get(x).map(move |x| *x)) + .collect() + } + + /// Overwrites a column in the grid. + /// + /// Returns [None] if x is out of bounds or `col` is not of the correct size. + pub fn set_col(&mut self, x: usize, col: &[T]) -> Option<()> { + if col.len() != self.height() { + return None; + } + let width = self.width(); + if self + .data + .chunks_exact_mut(width) + .zip(col.iter()) + .map(|(row, column_value)| { + row.get_mut(x).map(move |cell| *cell = *column_value) + }) + .all(|cell| cell.is_some()) + { + Some(()) + } else { + None + } + } + + /// Overwrites a row in the grid. + /// + /// Returns [None] if y is out of bounds or `row` is not of the correct size. + pub fn set_row(&mut self, y: usize, row: &[T]) -> Option<()> { + let width = self.width(); + self.data.chunks_exact_mut(width).nth(y).and_then(|chunk| { + if chunk.len() == row.len() { + chunk.copy_from_slice(row); + Some(()) + } else { + None + } + }) + } } impl Grid for PrimitiveGrid { @@ -347,4 +405,28 @@ mod tests { assert_eq!(grid.get_optional(0, 0), Some(5)); assert_eq!(grid.get_optional(0, 8), None); } + + #[test] + fn col() { + let mut grid = PrimitiveGrid::load(2, 3, &[0, 1, 2, 3, 4, 5]); + assert_eq!(grid.get_col(0), Some(vec![0, 2, 4])); + assert_eq!(grid.get_col(1), Some(vec![1, 3, 5])); + assert_eq!(grid.get_col(2), None); + assert_eq!(grid.set_col(0, &[5, 7, 9]), Some(())); + assert_eq!(grid.set_col(2, &[5, 7, 9]), None); + assert_eq!(grid.set_col(0, &[5, 7]), None); + assert_eq!(grid.get_col(0), Some(vec![5, 7, 9])); + } + + #[test] + fn row() { + let mut grid = PrimitiveGrid::load(2, 3, &[0, 1, 2, 3, 4, 5]); + assert_eq!(grid.get_row(0), Some(vec![0, 1])); + assert_eq!(grid.get_row(2), Some(vec![4, 5])); + assert_eq!(grid.get_row(3), None); + assert_eq!(grid.set_row(0, &[5, 7]), Some(())); + assert_eq!(grid.get_row(0), Some(vec![5, 7])); + assert_eq!(grid.set_row(3, &[5, 7]), None); + assert_eq!(grid.set_row(2, &[5, 7, 3]), None); + } } From 01edc3dfcc18ca2358e999733789b3de6a0390ff Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sun, 10 Nov 2024 17:46:03 +0100 Subject: [PATCH 06/18] move CharGrid to own file --- crates/servicepoint/src/char_grid.rs | 4 ++++ crates/servicepoint/src/cp437.rs | 5 ++--- crates/servicepoint/src/lib.rs | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 crates/servicepoint/src/char_grid.rs diff --git a/crates/servicepoint/src/char_grid.rs b/crates/servicepoint/src/char_grid.rs new file mode 100644 index 0000000..1e7e80d --- /dev/null +++ b/crates/servicepoint/src/char_grid.rs @@ -0,0 +1,4 @@ +use crate::PrimitiveGrid; + +/// A grid containing UTF-8 characters. +pub type CharGrid = PrimitiveGrid; \ No newline at end of file diff --git a/crates/servicepoint/src/cp437.rs b/crates/servicepoint/src/cp437.rs index f757594..7b4d3af 100644 --- a/crates/servicepoint/src/cp437.rs +++ b/crates/servicepoint/src/cp437.rs @@ -10,9 +10,6 @@ use std::collections::HashMap; /// The encoding is currently not enforced. pub type Cp437Grid = PrimitiveGrid; -/// A grid containing UTF-8 characters. -pub type CharGrid = PrimitiveGrid; - /// Errors that can occur when loading CP-437. #[derive(Debug, PartialEq)] pub enum Cp437LoadError { @@ -91,6 +88,7 @@ pub use feature_cp437::*; #[cfg(feature = "cp437")] mod feature_cp437 { + use crate::CharGrid; use super::*; /// An array of 256 elements, mapping most of the CP437 values to UTF-8 characters @@ -248,6 +246,7 @@ mod tests { #[cfg(test)] #[cfg(feature = "cp437")] mod tests_feature_cp437 { + use crate::CharGrid; use super::*; #[test] diff --git a/crates/servicepoint/src/lib.rs b/crates/servicepoint/src/lib.rs index 3c1fc02..aee3bc3 100644 --- a/crates/servicepoint/src/lib.rs +++ b/crates/servicepoint/src/lib.rs @@ -41,10 +41,11 @@ pub use bitvec; pub use crate::bitmap::Bitmap; pub use crate::brightness::{Brightness, BrightnessGrid}; +pub use crate::char_grid::CharGrid; pub use crate::command::{Command, Offset}; pub use crate::compression_code::CompressionCode; pub use crate::connection::Connection; -pub use crate::cp437::{CharGrid, Cp437Grid}; +pub use crate::cp437::Cp437Grid; pub use crate::data_ref::DataRef; pub use crate::grid::Grid; pub use crate::origin::{Origin, Pixels, Tiles}; @@ -55,6 +56,7 @@ pub type BitVec = bitvec::prelude::BitVec; mod bitmap; mod brightness; +mod char_grid; mod command; mod command_code; mod compression; From 7a23fcb54b118a9a7abb2c75f6d4e61e44e32068 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sun, 10 Nov 2024 18:11:09 +0100 Subject: [PATCH 07/18] add char grid str methods --- crates/servicepoint/src/char_grid.rs | 55 +++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/crates/servicepoint/src/char_grid.rs b/crates/servicepoint/src/char_grid.rs index 1e7e80d..274a5dd 100644 --- a/crates/servicepoint/src/char_grid.rs +++ b/crates/servicepoint/src/char_grid.rs @@ -1,4 +1,57 @@ use crate::PrimitiveGrid; /// A grid containing UTF-8 characters. -pub type CharGrid = PrimitiveGrid; \ No newline at end of file +pub type CharGrid = PrimitiveGrid; + +impl CharGrid { + /// Copies a column from the grid as a String. + /// + /// Returns [None] if x is out of bounds. + pub fn get_col_str(&self, x: usize) -> Option { + Some(String::from_iter(self.get_col(x)?)) + } + + /// Copies a row from the grid as a String. + /// + /// Returns [None] if y is out of bounds. + pub fn get_row_str(&self, y: usize) -> Option { + Some(String::from_iter(self.get_row(y)?)) + } + + /// Overwrites a row in the grid with a str. + /// + /// Returns [None] if y is out of bounds or `row` is not of the correct size. + pub fn set_row_str(&mut self, y: usize, value: &str) -> Option<()> { + self.set_row(y, value.chars().collect::>().as_ref()) + } + + /// Overwrites a column in the grid with a str. + /// + /// Returns [None] if y is out of bounds or `row` is not of the correct size. + pub fn set_col_str(&mut self, x: usize, value: &str) -> Option<()> { + self.set_col(x, value.chars().collect::>().as_ref()) + } +} + +#[cfg(test)] +mod test { + use super::*; + #[test] + fn col_str() { + let mut grid = CharGrid::new(2, 3); + assert_eq!(grid.get_col_str(2), None); + assert_eq!(grid.get_col_str(1), Some(String::from("\0\0\0"))); + assert_eq!(grid.set_col_str(1, "abc"), Some(())); + assert_eq!(grid.get_col_str(1), Some(String::from("abc"))); + } + + #[test] + fn row_str() { + let mut grid = CharGrid::new(2, 3); + assert_eq!(grid.get_row_str(3), None); + assert_eq!(grid.get_row_str(1), Some(String::from("\0\0"))); + assert_eq!(grid.set_row_str(1, "abc"), None); + assert_eq!(grid.set_row_str(1, "ab"), Some(())); + assert_eq!(grid.get_row_str(1), Some(String::from("ab"))); + } +} From 98901f44794c03cd0be4267865fda87fcdc5647b Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Tue, 12 Nov 2024 19:37:21 +0100 Subject: [PATCH 08/18] err returns for series methods --- Cargo.toml | 3 + crates/servicepoint/Cargo.toml | 1 + crates/servicepoint/src/char_grid.rs | 29 ++++++-- crates/servicepoint/src/lib.rs | 2 +- crates/servicepoint/src/primitive_grid.rs | 84 +++++++++++++++++------ 5 files changed, 89 insertions(+), 30 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a8e0809..cda21d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,6 @@ version = "0.10.0" [workspace.lints.rust] missing-docs = "warn" + +[workspace.dependencies] +thiserror = "1.0.69" diff --git a/crates/servicepoint/Cargo.toml b/crates/servicepoint/Cargo.toml index f3d828d..3497c6c 100644 --- a/crates/servicepoint/Cargo.toml +++ b/crates/servicepoint/Cargo.toml @@ -22,6 +22,7 @@ rust-lzma = { version = "0.6.0", optional = true } rand = { version = "0.8", optional = true } tungstenite = { version = "0.24.0", optional = true } once_cell = { version = "1.20.2", optional = true } +thiserror.workspace = true [features] default = ["compression_lzma", "protocol_udp", "cp437"] diff --git a/crates/servicepoint/src/char_grid.rs b/crates/servicepoint/src/char_grid.rs index 274a5dd..225474e 100644 --- a/crates/servicepoint/src/char_grid.rs +++ b/crates/servicepoint/src/char_grid.rs @@ -1,3 +1,4 @@ +use crate::primitive_grid::SeriesError; use crate::PrimitiveGrid; /// A grid containing UTF-8 characters. @@ -20,15 +21,23 @@ impl CharGrid { /// Overwrites a row in the grid with a str. /// - /// Returns [None] if y is out of bounds or `row` is not of the correct size. - pub fn set_row_str(&mut self, y: usize, value: &str) -> Option<()> { + /// Returns [SeriesError] if y is out of bounds or `row` is not of the correct size. + pub fn set_row_str( + &mut self, + y: usize, + value: &str, + ) -> Result<(), SeriesError> { self.set_row(y, value.chars().collect::>().as_ref()) } /// Overwrites a column in the grid with a str. /// - /// Returns [None] if y is out of bounds or `row` is not of the correct size. - pub fn set_col_str(&mut self, x: usize, value: &str) -> Option<()> { + /// Returns [SeriesError] if y is out of bounds or `row` is not of the correct size. + pub fn set_col_str( + &mut self, + x: usize, + value: &str, + ) -> Result<(), SeriesError> { self.set_col(x, value.chars().collect::>().as_ref()) } } @@ -41,7 +50,7 @@ mod test { let mut grid = CharGrid::new(2, 3); assert_eq!(grid.get_col_str(2), None); assert_eq!(grid.get_col_str(1), Some(String::from("\0\0\0"))); - assert_eq!(grid.set_col_str(1, "abc"), Some(())); + assert_eq!(grid.set_col_str(1, "abc"), Ok(())); assert_eq!(grid.get_col_str(1), Some(String::from("abc"))); } @@ -50,8 +59,14 @@ mod test { let mut grid = CharGrid::new(2, 3); assert_eq!(grid.get_row_str(3), None); assert_eq!(grid.get_row_str(1), Some(String::from("\0\0"))); - assert_eq!(grid.set_row_str(1, "abc"), None); - assert_eq!(grid.set_row_str(1, "ab"), Some(())); + assert_eq!( + grid.set_row_str(1, "abc"), + Err(SeriesError::InvalidLength { + expected: 2, + actual: 3 + }) + ); + assert_eq!(grid.set_row_str(1, "ab"), Ok(())); assert_eq!(grid.get_row_str(1), Some(String::from("ab"))); } } diff --git a/crates/servicepoint/src/lib.rs b/crates/servicepoint/src/lib.rs index aee3bc3..46478e1 100644 --- a/crates/servicepoint/src/lib.rs +++ b/crates/servicepoint/src/lib.rs @@ -49,7 +49,7 @@ pub use crate::cp437::Cp437Grid; pub use crate::data_ref::DataRef; pub use crate::grid::Grid; pub use crate::origin::{Origin, Pixels, Tiles}; -pub use crate::primitive_grid::PrimitiveGrid; +pub use crate::primitive_grid::{PrimitiveGrid, SeriesError}; /// An alias for the specific type of [bitvec::prelude::BitVec] used. pub type BitVec = bitvec::prelude::BitVec; diff --git a/crates/servicepoint/src/primitive_grid.rs b/crates/servicepoint/src/primitive_grid.rs index 72bcfc0..93c4249 100644 --- a/crates/servicepoint/src/primitive_grid.rs +++ b/crates/servicepoint/src/primitive_grid.rs @@ -13,6 +13,14 @@ pub struct PrimitiveGrid { data: Vec, } +#[derive(thiserror::Error, Debug, PartialEq)] +pub enum SeriesError { + #[error("The index {index} was out of bounds for size {size}")] + OutOfBounds { index: usize, size: usize }, + #[error("The provided series was expected to have a length of {expected}, but was {actual}")] + InvalidLength { actual: usize, expected: usize }, +} + impl PrimitiveGrid { /// Creates a new [PrimitiveGrid] with the specified dimensions. /// @@ -155,16 +163,19 @@ impl PrimitiveGrid { pub fn get_col(&self, x: usize) -> Option> { self.data .chunks_exact(self.width()) - .map(|row| row.get(x).map(move |x| *x)) + .map(|row| row.get(x).copied()) .collect() } /// Overwrites a column in the grid. /// - /// Returns [None] if x is out of bounds or `col` is not of the correct size. - pub fn set_col(&mut self, x: usize, col: &[T]) -> Option<()> { + /// Returns [Err] if x is out of bounds or `col` is not of the correct size. + pub fn set_col(&mut self, x: usize, col: &[T]) -> Result<(), SeriesError> { if col.len() != self.height() { - return None; + return Err(SeriesError::InvalidLength { + expected: self.height(), + actual: col.len(), + }); } let width = self.width(); if self @@ -176,25 +187,36 @@ impl PrimitiveGrid { }) .all(|cell| cell.is_some()) { - Some(()) + Ok(()) } else { - None + Err(SeriesError::OutOfBounds {index: x, size: width}) } } /// Overwrites a row in the grid. /// - /// Returns [None] if y is out of bounds or `row` is not of the correct size. - pub fn set_row(&mut self, y: usize, row: &[T]) -> Option<()> { + /// Returns [Err] if y is out of bounds or `row` is not of the correct size. + pub fn set_row(&mut self, y: usize, row: &[T]) -> Result<(), SeriesError> { let width = self.width(); - self.data.chunks_exact_mut(width).nth(y).and_then(|chunk| { - if chunk.len() == row.len() { - chunk.copy_from_slice(row); - Some(()) - } else { - None + if row.len() != width { + return Err(SeriesError::InvalidLength { + expected: width, + actual: row.len(), + }); + } + + let chunk = match self.data.chunks_exact_mut(width).nth(y) { + Some(row) => row, + None => { + return Err(SeriesError::OutOfBounds { + size: self.height(), + index: y, + }) } - }) + }; + + chunk.copy_from_slice(row); + Ok(()) } } @@ -283,7 +305,7 @@ impl<'t, T: PrimitiveGridType> Iterator for IterRows<'t, T> { #[cfg(test)] mod tests { - use crate::{DataRef, Grid, PrimitiveGrid}; + use crate::{DataRef, Grid, PrimitiveGrid, SeriesError}; #[test] fn fill() { @@ -412,9 +434,18 @@ mod tests { assert_eq!(grid.get_col(0), Some(vec![0, 2, 4])); assert_eq!(grid.get_col(1), Some(vec![1, 3, 5])); assert_eq!(grid.get_col(2), None); - assert_eq!(grid.set_col(0, &[5, 7, 9]), Some(())); - assert_eq!(grid.set_col(2, &[5, 7, 9]), None); - assert_eq!(grid.set_col(0, &[5, 7]), None); + assert_eq!(grid.set_col(0, &[5, 7, 9]), Ok(())); + assert_eq!( + grid.set_col(2, &[5, 7, 9]), + Err(SeriesError::OutOfBounds { size: 2, index: 2 }) + ); + assert_eq!( + grid.set_col(0, &[5, 7]), + Err(SeriesError::InvalidLength { + expected: 3, + actual: 2 + }) + ); assert_eq!(grid.get_col(0), Some(vec![5, 7, 9])); } @@ -424,9 +455,18 @@ mod tests { assert_eq!(grid.get_row(0), Some(vec![0, 1])); assert_eq!(grid.get_row(2), Some(vec![4, 5])); assert_eq!(grid.get_row(3), None); - assert_eq!(grid.set_row(0, &[5, 7]), Some(())); + assert_eq!(grid.set_row(0, &[5, 7]), Ok(())); assert_eq!(grid.get_row(0), Some(vec![5, 7])); - assert_eq!(grid.set_row(3, &[5, 7]), None); - assert_eq!(grid.set_row(2, &[5, 7, 3]), None); + assert_eq!( + grid.set_row(3, &[5, 7]), + Err(SeriesError::OutOfBounds { size: 3, index: 3 }) + ); + assert_eq!( + grid.set_row(2, &[5, 7, 3]), + Err(SeriesError::InvalidLength { + expected: 2, + actual: 3 + }) + ); } } From c5df317629ff6de414de79f35431e1bef4b695da Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Tue, 12 Nov 2024 19:38:20 +0100 Subject: [PATCH 09/18] cargo update --- Cargo.lock | 97 +++++++++++++++++++++++++++--------------------------- 1 file changed, 49 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5bc28b4..7cd19aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -34,36 +34,36 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -101,9 +101,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" [[package]] name = "bzip2" @@ -147,9 +147,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.30" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" +checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8" dependencies = [ "jobserver", "libc", @@ -204,15 +204,15 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" dependencies = [ "libc", ] @@ -280,9 +280,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" [[package]] name = "flate2" @@ -329,9 +329,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" [[package]] name = "heck" @@ -403,9 +403,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.159" +version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" [[package]] name = "linux-raw-sys" @@ -457,9 +457,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.87" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] @@ -511,9 +511,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -523,9 +523,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -550,9 +550,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" dependencies = [ "bitflags", "errno", @@ -569,18 +569,18 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", @@ -589,9 +589,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", "memchr", @@ -620,6 +620,7 @@ dependencies = [ "once_cell", "rand", "rust-lzma", + "thiserror", "tungstenite", "zstd", ] @@ -666,9 +667,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.79" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -683,9 +684,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", "fastrand", @@ -696,18 +697,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", From eb7496b27da31d6d84fca985f46e494ee1e31a94 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Tue, 12 Nov 2024 19:43:32 +0100 Subject: [PATCH 10/18] fix missing doc warning --- crates/servicepoint/src/primitive_grid.rs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/crates/servicepoint/src/primitive_grid.rs b/crates/servicepoint/src/primitive_grid.rs index 93c4249..882a1a5 100644 --- a/crates/servicepoint/src/primitive_grid.rs +++ b/crates/servicepoint/src/primitive_grid.rs @@ -13,12 +13,25 @@ pub struct PrimitiveGrid { data: Vec, } +/// Error type for methods that change a whole column or row at once #[derive(thiserror::Error, Debug, PartialEq)] pub enum SeriesError { #[error("The index {index} was out of bounds for size {size}")] - OutOfBounds { index: usize, size: usize }, + /// The index {index} was out of bounds for size {size} + OutOfBounds { + /// the index where access was tried + index: usize, + /// the size in that direction + size: usize, + }, #[error("The provided series was expected to have a length of {expected}, but was {actual}")] - InvalidLength { actual: usize, expected: usize }, + /// The provided series was expected to have a length of {expected}, but was {actual} + InvalidLength { + /// actual size of the provided series + actual: usize, + /// expected size + expected: usize, + }, } impl PrimitiveGrid { @@ -189,7 +202,10 @@ impl PrimitiveGrid { { Ok(()) } else { - Err(SeriesError::OutOfBounds {index: x, size: width}) + Err(SeriesError::OutOfBounds { + index: x, + size: width, + }) } } From ab16f57292ddcbf9979fc1e16e89c23e33a6671a Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Tue, 12 Nov 2024 21:38:49 +0100 Subject: [PATCH 11/18] more From for Cp437Grid and BitVec --- Cargo.toml | 2 +- crates/servicepoint/examples/announce.rs | 6 +++--- crates/servicepoint/examples/wiping_clear.rs | 6 +----- crates/servicepoint/src/bitmap.rs | 6 ++++++ crates/servicepoint/src/cp437.rs | 12 ++++++++++++ 5 files changed, 23 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cda21d5..8eb7cfd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,4 @@ version = "0.10.0" missing-docs = "warn" [workspace.dependencies] -thiserror = "1.0.69" +thiserror = "1.0.69" \ No newline at end of file diff --git a/crates/servicepoint/examples/announce.rs b/crates/servicepoint/examples/announce.rs index af78a54..66227a4 100644 --- a/crates/servicepoint/examples/announce.rs +++ b/crates/servicepoint/examples/announce.rs @@ -42,10 +42,10 @@ fn main() { } let text = cli.text.join("\n"); - let grid = CharGrid::from(&*text); - let cp437_grid = Cp437Grid::from(&grid); + let grid = CharGrid::from(text); + let grid = Cp437Grid::from(grid); connection - .send(Command::Cp437Data(Origin::ZERO, cp437_grid)) + .send(Command::Cp437Data(Origin::ZERO, grid)) .expect("sending text failed"); } diff --git a/crates/servicepoint/examples/wiping_clear.rs b/crates/servicepoint/examples/wiping_clear.rs index a3bf04a..6fd202e 100644 --- a/crates/servicepoint/examples/wiping_clear.rs +++ b/crates/servicepoint/examples/wiping_clear.rs @@ -33,12 +33,8 @@ fn main() { enabled_pixels.set(x_offset % PIXEL_WIDTH, y, false); } - // this works because the pixel grid has max size - let pixel_data: Vec = enabled_pixels.clone().into(); - let bit_vec = BitVec::from_vec(pixel_data); - connection - .send(Command::BitmapLinearAnd(0, bit_vec, CompressionCode::Lzma)) + .send(Command::BitmapLinearWin(Origin::ZERO, enabled_pixels.clone(), CompressionCode::Lzma)) .expect("could not send command to display"); thread::sleep(sleep_duration); } diff --git a/crates/servicepoint/src/bitmap.rs b/crates/servicepoint/src/bitmap.rs index 697a0d5..3d23706 100644 --- a/crates/servicepoint/src/bitmap.rs +++ b/crates/servicepoint/src/bitmap.rs @@ -175,6 +175,12 @@ impl From for Vec { } } +impl From for BitVec { + fn from(value: Bitmap) -> Self { + value.bit_vec + } +} + pub struct IterRows<'t> { bitmap: &'t Bitmap, row: usize, diff --git a/crates/servicepoint/src/cp437.rs b/crates/servicepoint/src/cp437.rs index 7b4d3af..1c1fba9 100644 --- a/crates/servicepoint/src/cp437.rs +++ b/crates/servicepoint/src/cp437.rs @@ -141,6 +141,12 @@ mod feature_cp437 { } } + impl From for Cp437Grid { + fn from(value: CharGrid) -> Self { + Cp437Grid::from(&value) + } + } + impl From<&str> for CharGrid { fn from(value: &str) -> Self { let value = value.replace("\r\n", "\n"); @@ -166,6 +172,12 @@ mod feature_cp437 { } } + impl From for CharGrid { + fn from(value: String) -> Self { + CharGrid::from(&value) + } + } + impl From<&CharGrid> for String { fn from(value: &CharGrid) -> Self { value From 7fbfb9370e7d99240fe5008add3648326a1fff48 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Tue, 12 Nov 2024 22:02:50 +0100 Subject: [PATCH 12/18] move trait impls to CharGrid that are not encoding related --- crates/servicepoint/src/char_grid.rs | 59 ++++++++++++++++++++++++++- crates/servicepoint/src/cp437.rs | 60 +--------------------------- 2 files changed, 60 insertions(+), 59 deletions(-) diff --git a/crates/servicepoint/src/char_grid.rs b/crates/servicepoint/src/char_grid.rs index 225474e..1a9045c 100644 --- a/crates/servicepoint/src/char_grid.rs +++ b/crates/servicepoint/src/char_grid.rs @@ -1,5 +1,5 @@ use crate::primitive_grid::SeriesError; -use crate::PrimitiveGrid; +use crate::{Grid, PrimitiveGrid}; /// A grid containing UTF-8 characters. pub type CharGrid = PrimitiveGrid; @@ -42,9 +42,57 @@ impl CharGrid { } } +impl From<&str> for CharGrid { + fn from(value: &str) -> Self { + let value = value.replace("\r\n", "\n"); + let mut lines = value + .split('\n') + .map(move |line| line.trim_end()) + .collect::>(); + let width = + lines.iter().fold(0, move |a, x| std::cmp::max(a, x.len())); + + while lines.last().is_some_and(move |line| line.is_empty()) { + _ = lines.pop(); + } + + let mut grid = Self::new(width, lines.len()); + for (y, line) in lines.iter().enumerate() { + for (x, char) in line.chars().enumerate() { + grid.set(x, y, char); + } + } + + grid + } +} + +impl From for CharGrid { + fn from(value: String) -> Self { + CharGrid::from(&*value) + } +} + +impl From<&CharGrid> for String { + fn from(value: &CharGrid) -> Self { + value + .iter_rows() + .map(move |chars| { + chars + .collect::() + .replace('\0', " ") + .trim_end() + .to_string() + }) + .collect::>() + .join("\n") + } +} + #[cfg(test)] mod test { use super::*; + use crate::Grid; #[test] fn col_str() { let mut grid = CharGrid::new(2, 3); @@ -69,4 +117,13 @@ mod test { assert_eq!(grid.set_row_str(1, "ab"), Ok(())); assert_eq!(grid.get_row_str(1), Some(String::from("ab"))); } + + #[test] + fn str_to_char_grid() { + let original = "Hello\r\nWorld!\n...\n"; + let grid = CharGrid::from(original); + assert_eq!(3, grid.height()); + let actual = String::from(&grid); + assert_eq!("Hello\nWorld!\n...", actual); + } } diff --git a/crates/servicepoint/src/cp437.rs b/crates/servicepoint/src/cp437.rs index 1c1fba9..9a81717 100644 --- a/crates/servicepoint/src/cp437.rs +++ b/crates/servicepoint/src/cp437.rs @@ -88,8 +88,8 @@ pub use feature_cp437::*; #[cfg(feature = "cp437")] mod feature_cp437 { - use crate::CharGrid; use super::*; + use crate::CharGrid; /// An array of 256 elements, mapping most of the CP437 values to UTF-8 characters /// @@ -147,53 +147,6 @@ mod feature_cp437 { } } - impl From<&str> for CharGrid { - fn from(value: &str) -> Self { - let value = value.replace("\r\n", "\n"); - let mut lines = value - .split('\n') - .map(move |line| line.trim_end()) - .collect::>(); - let width = - lines.iter().fold(0, move |a, x| std::cmp::max(a, x.len())); - - while lines.last().is_some_and(move |line| line.is_empty()) { - _ = lines.pop(); - } - - let mut grid = Self::new(width, lines.len()); - for (y, line) in lines.iter().enumerate() { - for (x, char) in line.chars().enumerate() { - grid.set(x, y, char); - } - } - - grid - } - } - - impl From for CharGrid { - fn from(value: String) -> Self { - CharGrid::from(&value) - } - } - - impl From<&CharGrid> for String { - fn from(value: &CharGrid) -> Self { - value - .iter_rows() - .map(move |chars| { - chars - .collect::() - .replace('\0', " ") - .trim_end() - .to_string() - }) - .collect::>() - .join("\n") - } - } - /// Convert the provided bytes to UTF-8. pub fn cp437_to_str(cp437: &[u8]) -> String { cp437.iter().map(move |char| cp437_to_char(*char)).collect() @@ -258,8 +211,8 @@ mod tests { #[cfg(test)] #[cfg(feature = "cp437")] mod tests_feature_cp437 { - use crate::CharGrid; use super::*; + use crate::CharGrid; #[test] fn round_trip_cp437() { @@ -303,13 +256,4 @@ mod tests_feature_cp437 { fn convert_invalid() { assert_eq!(cp437_to_char(char_to_cp437('😜')), '?'); } - - #[test] - fn str_to_char_grid() { - let original = "Hello\r\nWorld!\n...\n"; - let grid = CharGrid::from(original); - assert_eq!(3, grid.height()); - let actual = String::from(&grid); - assert_eq!("Hello\nWorld!\n...", actual); - } } From 0ca1b67cb6c0bbf68c8d0a7d3add640c63edcdb8 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Tue, 12 Nov 2024 22:11:45 +0100 Subject: [PATCH 13/18] fix broken links in documentation --- crates/servicepoint/src/cp437.rs | 2 +- crates/servicepoint/src/primitive_grid.rs | 2 ++ crates/servicepoint_binding_c/src/bitmap.rs | 2 ++ crates/servicepoint_binding_c/src/bitvec.rs | 2 ++ .../src/brightness_grid.rs | 4 ++- crates/servicepoint_binding_c/src/command.rs | 24 +++++++------ .../servicepoint_binding_c/src/cp437_grid.rs | 4 +++ .../ServicePoint/BindGen/ServicePoint.g.cs | 34 ++++++++++++------- 8 files changed, 49 insertions(+), 25 deletions(-) diff --git a/crates/servicepoint/src/cp437.rs b/crates/servicepoint/src/cp437.rs index 9a81717..194e3f6 100644 --- a/crates/servicepoint/src/cp437.rs +++ b/crates/servicepoint/src/cp437.rs @@ -97,7 +97,7 @@ mod feature_cp437 { /// /// See /// - /// Mostly copied from https://github.com/kip93/cp437-tools. License: GPL-3.0 + /// Mostly copied from . License: GPL-3.0 #[rustfmt::skip] pub const CP437_TO_UTF8: [char; 256] = [ /* 0X */ '\0', '☺', '☻', '♥', '♦', '♣', '♠', '•', '◘', '○', '\n', '♂', '♀', '♪', '♫', '☼', diff --git a/crates/servicepoint/src/primitive_grid.rs b/crates/servicepoint/src/primitive_grid.rs index 882a1a5..366f0ca 100644 --- a/crates/servicepoint/src/primitive_grid.rs +++ b/crates/servicepoint/src/primitive_grid.rs @@ -147,6 +147,8 @@ impl PrimitiveGrid { /// let grid: BrightnessGrid = grid.map(Brightness::saturating_from); /// let command = Command::CharBrightness(Origin::ZERO, grid); /// ``` + /// [Brightness]: [crate::Brightness] + /// [Command]: [crate::Command] pub fn map(&self, f: F) -> PrimitiveGrid where TConverted: PrimitiveGridType, diff --git a/crates/servicepoint_binding_c/src/bitmap.rs b/crates/servicepoint_binding_c/src/bitmap.rs index b327fd2..a7ae48a 100644 --- a/crates/servicepoint_binding_c/src/bitmap.rs +++ b/crates/servicepoint_binding_c/src/bitmap.rs @@ -124,6 +124,8 @@ pub unsafe extern "C" fn sp_bitmap_clone( /// - `bitmap` points to a valid [SPBitmap] /// - `bitmap` is not used concurrently or after bitmap call /// - `bitmap` was not passed to another consuming function, e.g. to create a [SPCommand] +/// +/// [SPCommand]: [crate::SPCommand] #[no_mangle] pub unsafe extern "C" fn sp_bitmap_free(bitmap: *mut SPBitmap) { assert!(!bitmap.is_null()); diff --git a/crates/servicepoint_binding_c/src/bitvec.rs b/crates/servicepoint_binding_c/src/bitvec.rs index 7f2258a..4ee5f52 100644 --- a/crates/servicepoint_binding_c/src/bitvec.rs +++ b/crates/servicepoint_binding_c/src/bitvec.rs @@ -123,6 +123,8 @@ pub unsafe extern "C" fn sp_bitvec_clone( /// - `bit_vec` points to a valid [SPBitVec] /// - `bit_vec` is not used concurrently or after this call /// - `bit_vec` was not passed to another consuming function, e.g. to create a [SPCommand] +/// +/// [SPCommand]: [crate::SPCommand] #[no_mangle] pub unsafe extern "C" fn sp_bitvec_free(bit_vec: *mut SPBitVec) { assert!(!bit_vec.is_null()); diff --git a/crates/servicepoint_binding_c/src/brightness_grid.rs b/crates/servicepoint_binding_c/src/brightness_grid.rs index 3c94b57..04187d7 100644 --- a/crates/servicepoint_binding_c/src/brightness_grid.rs +++ b/crates/servicepoint_binding_c/src/brightness_grid.rs @@ -133,6 +133,8 @@ pub unsafe extern "C" fn sp_brightness_grid_clone( /// - `brightness_grid` points to a valid [SPBrightnessGrid] /// - `brightness_grid` is not used concurrently or after this call /// - `brightness_grid` was not passed to another consuming function, e.g. to create a [SPCommand] +/// +/// [SPCommand]: [crate::SPCommand] #[no_mangle] pub unsafe extern "C" fn sp_brightness_grid_free( brightness_grid: *mut SPBrightnessGrid, @@ -191,7 +193,7 @@ pub unsafe extern "C" fn sp_brightness_grid_get( /// /// The caller has to make sure that: /// -/// - `brightness_grid` points to a valid [SPBitVec] +/// - `brightness_grid` points to a valid [SPBrightnessGrid] /// - `brightness_grid` is not written to or read from concurrently #[no_mangle] pub unsafe extern "C" fn sp_brightness_grid_set( diff --git a/crates/servicepoint_binding_c/src/command.rs b/crates/servicepoint_binding_c/src/command.rs index a4ebc1a..1c56d07 100644 --- a/crates/servicepoint_binding_c/src/command.rs +++ b/crates/servicepoint_binding_c/src/command.rs @@ -23,6 +23,8 @@ use crate::{ /// sp_connection_send_command(connection, sp_command_clear()); /// sp_connection_send_command(connection, sp_command_brightness(5)); /// ``` +/// +/// [SPConnection]: [crate::SPConnection] pub struct SPCommand(pub(crate) servicepoint::Command); impl Clone for SPCommand { @@ -90,7 +92,7 @@ pub unsafe extern "C" fn sp_command_clone( /// /// Does not affect brightness. /// -/// Returns: a new [Command::Clear] instance. Will never return NULL. +/// Returns: a new [servicepoint::Command::Clear] instance. Will never return NULL. /// /// # Examples /// @@ -114,7 +116,7 @@ pub unsafe extern "C" fn sp_command_clear() -> NonNull { /// /// Please do not send this in your normal program flow. /// -/// Returns: a new [Command::HardReset] instance. Will never return NULL. +/// Returns: a new [servicepoint::Command::HardReset] instance. Will never return NULL. /// /// # Safety /// @@ -130,7 +132,7 @@ pub unsafe extern "C" fn sp_command_hard_reset() -> NonNull { /// A yet-to-be-tested command. /// -/// Returns: a new `Command::FadeOut` instance. Will never return NULL. +/// Returns: a new [servicepoint::Command::FadeOut] instance. Will never return NULL. /// /// # Safety /// @@ -146,7 +148,7 @@ pub unsafe extern "C" fn sp_command_fade_out() -> NonNull { /// Set the brightness of all tiles to the same value. /// -/// Returns: a new [Command::Brightness] instance. Will never return NULL. +/// Returns: a new [servicepoint::Command::Brightness] instance. Will never return NULL. /// /// # Panics /// @@ -174,7 +176,7 @@ pub unsafe extern "C" fn sp_command_brightness( /// /// The passed [SPBrightnessGrid] gets consumed. /// -/// Returns: a new [Command::CharBrightness] instance. Will never return NULL. +/// Returns: a new [servicepoint::Command::CharBrightness] instance. Will never return NULL. /// /// # Panics /// @@ -211,7 +213,7 @@ pub unsafe extern "C" fn sp_command_char_brightness( /// /// The passed [SPBitVec] gets consumed. /// -/// Returns: a new [Command::BitmapLinear] instance. Will never return NULL. +/// Returns: a new [servicepoint::Command::BitmapLinear] instance. Will never return NULL. /// /// # Panics /// @@ -254,7 +256,7 @@ pub unsafe extern "C" fn sp_command_bitmap_linear( /// /// The passed [SPBitVec] gets consumed. /// -/// Returns: a new [Command::BitmapLinearAnd] instance. Will never return NULL. +/// Returns: a new [servicepoint::Command::BitmapLinearAnd] instance. Will never return NULL. /// /// # Panics /// @@ -297,7 +299,7 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_and( /// /// The passed [SPBitVec] gets consumed. /// -/// Returns: a new [Command::BitmapLinearOr] instance. Will never return NULL. +/// Returns: a new [servicepoint::Command::BitmapLinearOr] instance. Will never return NULL. /// /// # Panics /// @@ -340,7 +342,7 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_or( /// /// The passed [SPBitVec] gets consumed. /// -/// Returns: a new [Command::BitmapLinearXor] instance. Will never return NULL. +/// Returns: a new [servicepoint::Command::BitmapLinearXor] instance. Will never return NULL. /// /// # Panics /// @@ -378,7 +380,7 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_xor( /// /// The passed [SPCp437Grid] gets consumed. /// -/// Returns: a new [Command::Cp437Data] instance. Will never return NULL. +/// Returns: a new [servicepoint::Command::Cp437Data] instance. Will never return NULL. /// /// # Panics /// @@ -410,7 +412,7 @@ pub unsafe extern "C" fn sp_command_cp437_data( /// /// The passed [SPBitmap] gets consumed. /// -/// Returns: a new [Command::BitmapLinearWin] instance. Will never return NULL. +/// Returns: a new [servicepoint::Command::BitmapLinearWin] instance. Will never return NULL. /// /// # Panics /// diff --git a/crates/servicepoint_binding_c/src/cp437_grid.rs b/crates/servicepoint_binding_c/src/cp437_grid.rs index b940ae4..32e2642 100644 --- a/crates/servicepoint_binding_c/src/cp437_grid.rs +++ b/crates/servicepoint_binding_c/src/cp437_grid.rs @@ -117,6 +117,8 @@ pub unsafe extern "C" fn sp_cp437_grid_clone( /// - `cp437_grid` points to a valid [SPCp437Grid] /// - `cp437_grid` is not used concurrently or after cp437_grid call /// - `cp437_grid` was not passed to another consuming function, e.g. to create a [SPCommand] +/// +/// [SPCommand]: [crate::SPCommand] #[no_mangle] pub unsafe extern "C" fn sp_cp437_grid_free(cp437_grid: *mut SPCp437Grid) { assert!(!cp437_grid.is_null()); @@ -172,6 +174,8 @@ pub unsafe extern "C" fn sp_cp437_grid_get( /// /// - `cp437_grid` points to a valid [SPBitVec] /// - `cp437_grid` is not written to or read from concurrently +/// +/// [SPBitVec]: [crate::SPBitVec] #[no_mangle] pub unsafe extern "C" fn sp_cp437_grid_set( cp437_grid: *mut SPCp437Grid, diff --git a/crates/servicepoint_binding_cs/ServicePoint/BindGen/ServicePoint.g.cs b/crates/servicepoint_binding_cs/ServicePoint/BindGen/ServicePoint.g.cs index c8bb0a2..8f41ab4 100644 --- a/crates/servicepoint_binding_cs/ServicePoint/BindGen/ServicePoint.g.cs +++ b/crates/servicepoint_binding_cs/ServicePoint/BindGen/ServicePoint.g.cs @@ -108,6 +108,8 @@ namespace ServicePoint.BindGen /// - `bitmap` points to a valid [SPBitmap] /// - `bitmap` is not used concurrently or after bitmap call /// - `bitmap` was not passed to another consuming function, e.g. to create a [SPCommand] + /// + /// [SPCommand]: [crate::SPCommand] /// [DllImport(__DllName, EntryPoint = "sp_bitmap_free", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern void sp_bitmap_free(Bitmap* bitmap); @@ -321,6 +323,8 @@ namespace ServicePoint.BindGen /// - `bit_vec` points to a valid [SPBitVec] /// - `bit_vec` is not used concurrently or after this call /// - `bit_vec` was not passed to another consuming function, e.g. to create a [SPCommand] + /// + /// [SPCommand]: [crate::SPCommand] /// [DllImport(__DllName, EntryPoint = "sp_bitvec_free", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern void sp_bitvec_free(BitVec* bit_vec); @@ -540,6 +544,8 @@ namespace ServicePoint.BindGen /// - `brightness_grid` points to a valid [SPBrightnessGrid] /// - `brightness_grid` is not used concurrently or after this call /// - `brightness_grid` was not passed to another consuming function, e.g. to create a [SPCommand] + /// + /// [SPCommand]: [crate::SPCommand] /// [DllImport(__DllName, EntryPoint = "sp_brightness_grid_free", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern void sp_brightness_grid_free(BrightnessGrid* brightness_grid); @@ -590,7 +596,7 @@ namespace ServicePoint.BindGen /// /// The caller has to make sure that: /// - /// - `brightness_grid` points to a valid [SPBitVec] + /// - `brightness_grid` points to a valid [SPBrightnessGrid] /// - `brightness_grid` is not written to or read from concurrently /// [DllImport(__DllName, EntryPoint = "sp_brightness_grid_set", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] @@ -737,7 +743,7 @@ namespace ServicePoint.BindGen /// /// Does not affect brightness. /// - /// Returns: a new [Command::Clear] instance. Will never return NULL. + /// Returns: a new [servicepoint::Command::Clear] instance. Will never return NULL. /// /// # Examples /// @@ -760,7 +766,7 @@ namespace ServicePoint.BindGen /// /// Please do not send this in your normal program flow. /// - /// Returns: a new [Command::HardReset] instance. Will never return NULL. + /// Returns: a new [servicepoint::Command::HardReset] instance. Will never return NULL. /// /// # Safety /// @@ -775,7 +781,7 @@ namespace ServicePoint.BindGen /// /// A yet-to-be-tested command. /// - /// Returns: a new `Command::FadeOut` instance. Will never return NULL. + /// Returns: a new [servicepoint::Command::FadeOut] instance. Will never return NULL. /// /// # Safety /// @@ -790,7 +796,7 @@ namespace ServicePoint.BindGen /// /// Set the brightness of all tiles to the same value. /// - /// Returns: a new [Command::Brightness] instance. Will never return NULL. + /// Returns: a new [servicepoint::Command::Brightness] instance. Will never return NULL. /// /// # Panics /// @@ -811,7 +817,7 @@ namespace ServicePoint.BindGen /// /// The passed [SPBrightnessGrid] gets consumed. /// - /// Returns: a new [Command::CharBrightness] instance. Will never return NULL. + /// Returns: a new [servicepoint::Command::CharBrightness] instance. Will never return NULL. /// /// # Panics /// @@ -839,7 +845,7 @@ namespace ServicePoint.BindGen /// /// The passed [SPBitVec] gets consumed. /// - /// Returns: a new [Command::BitmapLinear] instance. Will never return NULL. + /// Returns: a new [servicepoint::Command::BitmapLinear] instance. Will never return NULL. /// /// # Panics /// @@ -869,7 +875,7 @@ namespace ServicePoint.BindGen /// /// The passed [SPBitVec] gets consumed. /// - /// Returns: a new [Command::BitmapLinearAnd] instance. Will never return NULL. + /// Returns: a new [servicepoint::Command::BitmapLinearAnd] instance. Will never return NULL. /// /// # Panics /// @@ -899,7 +905,7 @@ namespace ServicePoint.BindGen /// /// The passed [SPBitVec] gets consumed. /// - /// Returns: a new [Command::BitmapLinearOr] instance. Will never return NULL. + /// Returns: a new [servicepoint::Command::BitmapLinearOr] instance. Will never return NULL. /// /// # Panics /// @@ -929,7 +935,7 @@ namespace ServicePoint.BindGen /// /// The passed [SPBitVec] gets consumed. /// - /// Returns: a new [Command::BitmapLinearXor] instance. Will never return NULL. + /// Returns: a new [servicepoint::Command::BitmapLinearXor] instance. Will never return NULL. /// /// # Panics /// @@ -954,7 +960,7 @@ namespace ServicePoint.BindGen /// /// The passed [SPCp437Grid] gets consumed. /// - /// Returns: a new [Command::Cp437Data] instance. Will never return NULL. + /// Returns: a new [servicepoint::Command::Cp437Data] instance. Will never return NULL. /// /// # Panics /// @@ -977,7 +983,7 @@ namespace ServicePoint.BindGen /// /// The passed [SPBitmap] gets consumed. /// - /// Returns: a new [Command::BitmapLinearWin] instance. Will never return NULL. + /// Returns: a new [servicepoint::Command::BitmapLinearWin] instance. Will never return NULL. /// /// # Panics /// @@ -1178,6 +1184,8 @@ namespace ServicePoint.BindGen /// - `cp437_grid` points to a valid [SPCp437Grid] /// - `cp437_grid` is not used concurrently or after cp437_grid call /// - `cp437_grid` was not passed to another consuming function, e.g. to create a [SPCommand] + /// + /// [SPCommand]: [crate::SPCommand] /// [DllImport(__DllName, EntryPoint = "sp_cp437_grid_free", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern void sp_cp437_grid_free(Cp437Grid* cp437_grid); @@ -1227,6 +1235,8 @@ namespace ServicePoint.BindGen /// /// - `cp437_grid` points to a valid [SPBitVec] /// - `cp437_grid` is not written to or read from concurrently + /// + /// [SPBitVec]: [crate::SPBitVec] /// [DllImport(__DllName, EntryPoint = "sp_cp437_grid_set", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern void sp_cp437_grid_set(Cp437Grid* cp437_grid, nuint x, nuint y, byte value); From 2e8a61b627540df951d5a7cf7eb0f6dbc43ba1e1 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Tue, 12 Nov 2024 22:33:30 +0100 Subject: [PATCH 14/18] use thiserror for more errors --- crates/servicepoint/examples/wiping_clear.rs | 2 +- crates/servicepoint/src/command.rs | 8 +++++- crates/servicepoint/src/connection.rs | 28 +++++++++++++------- crates/servicepoint/src/cp437.rs | 24 ++++++++--------- 4 files changed, 38 insertions(+), 24 deletions(-) diff --git a/crates/servicepoint/examples/wiping_clear.rs b/crates/servicepoint/examples/wiping_clear.rs index 6fd202e..6d85724 100644 --- a/crates/servicepoint/examples/wiping_clear.rs +++ b/crates/servicepoint/examples/wiping_clear.rs @@ -4,7 +4,7 @@ use std::time::Duration; use clap::Parser; -use servicepoint::{bitvec::prelude::BitVec, *}; +use servicepoint::*; #[derive(Parser, Debug)] struct Cli { diff --git a/crates/servicepoint/src/command.rs b/crates/servicepoint/src/command.rs index 8d42530..462e448 100644 --- a/crates/servicepoint/src/command.rs +++ b/crates/servicepoint/src/command.rs @@ -212,21 +212,27 @@ pub enum Command { } /// Err values for [Command::try_from]. -#[derive(Debug, PartialEq)] +#[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), } diff --git a/crates/servicepoint/src/connection.rs b/crates/servicepoint/src/connection.rs index 721583c..087afd7 100644 --- a/crates/servicepoint/src/connection.rs +++ b/crates/servicepoint/src/connection.rs @@ -1,5 +1,5 @@ -use std::fmt::Debug; use crate::packet::Packet; +use std::fmt::Debug; /// A connection to the display. /// @@ -34,20 +34,26 @@ pub enum Connection { /// [servicepoint-websocket-relay]: https://github.com/kaesaecracker/servicepoint-websocket-relay #[cfg(feature = "protocol_websocket")] WebSocket( - std::sync::Arc, - >>>, + std::sync::Arc< + std::sync::Mutex< + tungstenite::WebSocket< + tungstenite::stream::MaybeTlsStream, + >, + >, + >, ), /// A fake connection for testing that does not actually send anything. Fake, } -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] pub enum SendError { - IoError(std::io::Error), + #[error("IO error occurred while sending")] + IoError(#[from] std::io::Error), #[cfg(feature = "protocol_websocket")] - WebsocketError(tungstenite::Error), + #[error("WebSocket error occurred while sending")] + WebsocketError(#[from] tungstenite::Error), } impl Connection { @@ -103,7 +109,9 @@ impl Connection { let request = ClientRequestBuilder::new(uri).into_client_request()?; let (sock, _) = connect(request)?; - Ok(Self::WebSocket(std::sync::Arc::new(std::sync::Mutex::new(sock)))) + Ok(Self::WebSocket(std::sync::Arc::new(std::sync::Mutex::new( + sock, + )))) } /// Send something packet-like to the display. Usually this is in the form of a Command. @@ -157,7 +165,9 @@ impl Drop for Connection { fn drop(&mut self) { #[cfg(feature = "protocol_websocket")] if let Connection::WebSocket(sock) = self { - _ = sock.try_lock().map(move |mut sock| sock.close(None).unwrap() ); + _ = sock + .try_lock() + .map(move |mut sock| sock.close(None).unwrap()); } } } diff --git a/crates/servicepoint/src/cp437.rs b/crates/servicepoint/src/cp437.rs index 194e3f6..bfd8b5a 100644 --- a/crates/servicepoint/src/cp437.rs +++ b/crates/servicepoint/src/cp437.rs @@ -10,16 +10,14 @@ use std::collections::HashMap; /// The encoding is currently not enforced. pub type Cp437Grid = PrimitiveGrid; -/// Errors that can occur when loading CP-437. -#[derive(Debug, PartialEq)] -pub enum Cp437LoadError { - /// Invalid character in input prevented loading - InvalidChar { - /// invalid character is at this position in input - index: usize, - /// the invalid character - char: char, - }, +/// The error occurring when loading an invalid character +#[derive(Debug, PartialEq, thiserror::Error)] +#[error("The character {char:?} at position {index} is not a valid CP437 character")] +pub struct InvalidCharError { + /// invalid character is at this position in input + index: usize, + /// the invalid character + char: char, } impl Cp437Grid { @@ -33,7 +31,7 @@ impl Cp437Grid { value: &str, width: usize, wrap: bool, - ) -> Result { + ) -> Result { assert!(width > 0); assert!(!value.is_empty()); @@ -43,7 +41,7 @@ impl Cp437Grid { for (index, char) in value.chars().enumerate() { if !char.is_ascii() { - return Err(Cp437LoadError::InvalidChar { index, char }); + return Err(InvalidCharError { index, char }); } let is_lf = char == '\n'; @@ -199,7 +197,7 @@ mod tests { #[test] fn load_ascii_invalid() { assert_eq!( - Err(Cp437LoadError::InvalidChar { + Err(InvalidCharError { char: '🥶', index: 2 }), From bb34541077e5067f3ac3d729dc14ea89fd80e5fa Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Wed, 13 Nov 2024 18:13:26 +0100 Subject: [PATCH 15/18] remove unreachable path --- crates/servicepoint/src/connection.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/servicepoint/src/connection.rs b/crates/servicepoint/src/connection.rs index 087afd7..3626bc8 100644 --- a/crates/servicepoint/src/connection.rs +++ b/crates/servicepoint/src/connection.rs @@ -153,10 +153,6 @@ impl Connection { let _ = data; Ok(()) } - #[allow(unreachable_patterns)] // depends on features - _ => { - panic!("Connection {:?} does not support immutable send", self) - } } } } From 3d74bfe05e72b5db540aef324d6456f3e836c729 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Wed, 13 Nov 2024 18:29:48 +0100 Subject: [PATCH 16/18] remove Arc from WebSocket Connection --- crates/servicepoint/src/connection.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/crates/servicepoint/src/connection.rs b/crates/servicepoint/src/connection.rs index 3626bc8..9d53611 100644 --- a/crates/servicepoint/src/connection.rs +++ b/crates/servicepoint/src/connection.rs @@ -34,11 +34,9 @@ pub enum Connection { /// [servicepoint-websocket-relay]: https://github.com/kaesaecracker/servicepoint-websocket-relay #[cfg(feature = "protocol_websocket")] WebSocket( - std::sync::Arc< - std::sync::Mutex< - tungstenite::WebSocket< - tungstenite::stream::MaybeTlsStream, - >, + std::sync::Mutex< + tungstenite::WebSocket< + tungstenite::stream::MaybeTlsStream, >, >, ), @@ -109,9 +107,9 @@ impl Connection { let request = ClientRequestBuilder::new(uri).into_client_request()?; let (sock, _) = connect(request)?; - Ok(Self::WebSocket(std::sync::Arc::new(std::sync::Mutex::new( + Ok(Self::WebSocket(std::sync::Mutex::new( sock, - )))) + ))) } /// Send something packet-like to the display. Usually this is in the form of a Command. @@ -163,7 +161,7 @@ impl Drop for Connection { if let Connection::WebSocket(sock) = self { _ = sock .try_lock() - .map(move |mut sock| sock.close(None).unwrap()); + .map(move |mut sock| sock.close(None)); } } } From eed6d114fcbdbf5bf11571d7f861f6f56a91bd2f Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Wed, 13 Nov 2024 18:36:59 +0100 Subject: [PATCH 17/18] regenerate C header --- .../examples/lang_c/include/servicepoint.h | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/crates/servicepoint_binding_c/examples/lang_c/include/servicepoint.h b/crates/servicepoint_binding_c/examples/lang_c/include/servicepoint.h index fec0ff0..0d716d1 100644 --- a/crates/servicepoint_binding_c/examples/lang_c/include/servicepoint.h +++ b/crates/servicepoint_binding_c/examples/lang_c/include/servicepoint.h @@ -144,6 +144,8 @@ typedef struct SPBrightnessGrid SPBrightnessGrid; * sp_connection_send_command(connection, sp_command_clear()); * sp_connection_send_command(connection, sp_command_brightness(5)); * ``` + * + * [SPConnection]: [crate::SPConnection] */ typedef struct SPCommand SPCommand; @@ -266,6 +268,8 @@ void sp_bitmap_fill(SPBitmap *bitmap, bool value); * - `bitmap` points to a valid [SPBitmap] * - `bitmap` is not used concurrently or after bitmap call * - `bitmap` was not passed to another consuming function, e.g. to create a [SPCommand] + * + * [SPCommand]: [crate::SPCommand] */ void sp_bitmap_free(SPBitmap *bitmap); @@ -479,6 +483,8 @@ void sp_bitvec_fill(SPBitVec *bit_vec, bool value); * - `bit_vec` points to a valid [SPBitVec] * - `bit_vec` is not used concurrently or after this call * - `bit_vec` was not passed to another consuming function, e.g. to create a [SPCommand] + * + * [SPCommand]: [crate::SPCommand] */ void sp_bitvec_free(SPBitVec *bit_vec); @@ -695,6 +701,8 @@ void sp_brightness_grid_fill(SPBrightnessGrid *brightness_grid, uint8_t value); * - `brightness_grid` points to a valid [SPBrightnessGrid] * - `brightness_grid` is not used concurrently or after this call * - `brightness_grid` was not passed to another consuming function, e.g. to create a [SPCommand] + * + * [SPCommand]: [crate::SPCommand] */ void sp_brightness_grid_free(SPBrightnessGrid *brightness_grid); @@ -805,7 +813,7 @@ SPBrightnessGrid *sp_brightness_grid_new(size_t width, * * The caller has to make sure that: * - * - `brightness_grid` points to a valid [SPBitVec] + * - `brightness_grid` points to a valid [SPBrightnessGrid] * - `brightness_grid` is not written to or read from concurrently */ void sp_brightness_grid_set(SPBrightnessGrid *brightness_grid, @@ -867,7 +875,7 @@ size_t sp_brightness_grid_width(const SPBrightnessGrid *brightness_grid); * * The passed [SPBitVec] gets consumed. * - * Returns: a new [Command::BitmapLinear] instance. Will never return NULL. + * Returns: a new [servicepoint::Command::BitmapLinear] instance. Will never return NULL. * * # Panics * @@ -898,7 +906,7 @@ SPCommand *sp_command_bitmap_linear(size_t offset, * * The passed [SPBitVec] gets consumed. * - * Returns: a new [Command::BitmapLinearAnd] instance. Will never return NULL. + * Returns: a new [servicepoint::Command::BitmapLinearAnd] instance. Will never return NULL. * * # Panics * @@ -929,7 +937,7 @@ SPCommand *sp_command_bitmap_linear_and(size_t offset, * * The passed [SPBitVec] gets consumed. * - * Returns: a new [Command::BitmapLinearOr] instance. Will never return NULL. + * Returns: a new [servicepoint::Command::BitmapLinearOr] instance. Will never return NULL. * * # Panics * @@ -955,7 +963,7 @@ SPCommand *sp_command_bitmap_linear_or(size_t offset, * * The passed [SPBitmap] gets consumed. * - * Returns: a new [Command::BitmapLinearWin] instance. Will never return NULL. + * Returns: a new [servicepoint::Command::BitmapLinearWin] instance. Will never return NULL. * * # Panics * @@ -987,7 +995,7 @@ SPCommand *sp_command_bitmap_linear_win(size_t x, * * The passed [SPBitVec] gets consumed. * - * Returns: a new [Command::BitmapLinearXor] instance. Will never return NULL. + * Returns: a new [servicepoint::Command::BitmapLinearXor] instance. Will never return NULL. * * # Panics * @@ -1011,7 +1019,7 @@ SPCommand *sp_command_bitmap_linear_xor(size_t offset, /** * Set the brightness of all tiles to the same value. * - * Returns: a new [Command::Brightness] instance. Will never return NULL. + * Returns: a new [servicepoint::Command::Brightness] instance. Will never return NULL. * * # Panics * @@ -1031,7 +1039,7 @@ SPCommand *sp_command_brightness(uint8_t brightness); * * The passed [SPBrightnessGrid] gets consumed. * - * Returns: a new [Command::CharBrightness] instance. Will never return NULL. + * Returns: a new [servicepoint::Command::CharBrightness] instance. Will never return NULL. * * # Panics * @@ -1055,7 +1063,7 @@ SPCommand *sp_command_char_brightness(size_t x, * * Does not affect brightness. * - * Returns: a new [Command::Clear] instance. Will never return NULL. + * Returns: a new [servicepoint::Command::Clear] instance. Will never return NULL. * * # Examples * @@ -1097,7 +1105,7 @@ SPCommand *sp_command_clone(const SPCommand *command); * * The passed [SPCp437Grid] gets consumed. * - * Returns: a new [Command::Cp437Data] instance. Will never return NULL. + * Returns: a new [servicepoint::Command::Cp437Data] instance. Will never return NULL. * * # Panics * @@ -1119,7 +1127,7 @@ SPCommand *sp_command_cp437_data(size_t x, /** * A yet-to-be-tested command. * - * Returns: a new `Command::FadeOut` instance. Will never return NULL. + * Returns: a new [servicepoint::Command::FadeOut] instance. Will never return NULL. * * # Safety * @@ -1159,7 +1167,7 @@ void sp_command_free(SPCommand *command); * * Please do not send this in your normal program flow. * - * Returns: a new [Command::HardReset] instance. Will never return NULL. + * Returns: a new [servicepoint::Command::HardReset] instance. Will never return NULL. * * # Safety * @@ -1328,6 +1336,8 @@ void sp_cp437_grid_fill(SPCp437Grid *cp437_grid, uint8_t value); * - `cp437_grid` points to a valid [SPCp437Grid] * - `cp437_grid` is not used concurrently or after cp437_grid call * - `cp437_grid` was not passed to another consuming function, e.g. to create a [SPCommand] + * + * [SPCommand]: [crate::SPCommand] */ void sp_cp437_grid_free(SPCp437Grid *cp437_grid); @@ -1433,6 +1443,8 @@ SPCp437Grid *sp_cp437_grid_new(size_t width, * * - `cp437_grid` points to a valid [SPBitVec] * - `cp437_grid` is not written to or read from concurrently + * + * [SPBitVec]: [crate::SPBitVec] */ void sp_cp437_grid_set(SPCp437Grid *cp437_grid, size_t x, From 6e25e1754d865de187d14f8660a0c7c4a2716098 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Wed, 13 Nov 2024 18:49:50 +0100 Subject: [PATCH 18/18] version 0.11.0 --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- crates/servicepoint/README.md | 2 +- crates/servicepoint_binding_c/Cargo.toml | 2 +- crates/servicepoint_binding_cs/Cargo.toml | 4 ++-- .../ServicePoint/ServicePoint.csproj | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7cd19aa..01217c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -610,7 +610,7 @@ dependencies = [ [[package]] name = "servicepoint" -version = "0.10.0" +version = "0.11.0" dependencies = [ "bitvec", "bzip2", @@ -627,7 +627,7 @@ dependencies = [ [[package]] name = "servicepoint_binding_c" -version = "0.10.0" +version = "0.11.0" dependencies = [ "cbindgen", "servicepoint", @@ -635,7 +635,7 @@ dependencies = [ [[package]] name = "servicepoint_binding_cs" -version = "0.10.0" +version = "0.11.0" dependencies = [ "csbindgen", "servicepoint", diff --git a/Cargo.toml b/Cargo.toml index 8eb7cfd..9bc62fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ ] [workspace.package] -version = "0.10.0" +version = "0.11.0" [workspace.lints.rust] missing-docs = "warn" diff --git a/crates/servicepoint/README.md b/crates/servicepoint/README.md index 5d69a44..0a1064f 100644 --- a/crates/servicepoint/README.md +++ b/crates/servicepoint/README.md @@ -17,7 +17,7 @@ cargo add servicepoint or ```toml [dependencies] -servicepoint = "0.10.0" +servicepoint = "0.11.0" ``` ## Examples diff --git a/crates/servicepoint_binding_c/Cargo.toml b/crates/servicepoint_binding_c/Cargo.toml index d8dd0a1..91122d3 100644 --- a/crates/servicepoint_binding_c/Cargo.toml +++ b/crates/servicepoint_binding_c/Cargo.toml @@ -17,7 +17,7 @@ crate-type = ["staticlib", "cdylib", "rlib"] cbindgen = "0.27.0" [dependencies.servicepoint] -version = "0.10.0" +version = "0.11.0" path = "../servicepoint" features = ["all_compressions"] diff --git a/crates/servicepoint_binding_cs/Cargo.toml b/crates/servicepoint_binding_cs/Cargo.toml index caca7b9..01282ac 100644 --- a/crates/servicepoint_binding_cs/Cargo.toml +++ b/crates/servicepoint_binding_cs/Cargo.toml @@ -13,8 +13,8 @@ test = false csbindgen = "1.9.3" [dependencies] -servicepoint_binding_c = { version = "0.10.0", path = "../servicepoint_binding_c" } -servicepoint = { version = "0.10.0", path = "../servicepoint" } +servicepoint_binding_c = { version = "0.11.0", path = "../servicepoint_binding_c" } +servicepoint = { version = "0.11.0", path = "../servicepoint" } [lints] workspace = true diff --git a/crates/servicepoint_binding_cs/ServicePoint/ServicePoint.csproj b/crates/servicepoint_binding_cs/ServicePoint/ServicePoint.csproj index 0962c4c..e806186 100644 --- a/crates/servicepoint_binding_cs/ServicePoint/ServicePoint.csproj +++ b/crates/servicepoint_binding_cs/ServicePoint/ServicePoint.csproj @@ -11,7 +11,7 @@ ServicePoint - 0.10.0 + 0.11.0 Repository Authors None ServicePoint