diff --git a/Cargo.lock b/Cargo.lock index 486957e..8dfbf55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -426,8 +426,7 @@ dependencies = [ [[package]] name = "servicepoint" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91a33bff7f9db5008748b23ca0c906c276fe00694390b681f004a55968a42cfe" +source = "git+https://git.berlin.ccc.de/servicepoint/servicepoint.git?branch=next#5b6f88b1682925b7669b25a7fb08806c4009bbfd" dependencies = [ "bitvec", "bzip2", @@ -443,6 +442,7 @@ dependencies = [ name = "servicepoint_binding_uniffi" version = "0.15.0" dependencies = [ + "paste", "servicepoint", "thiserror 2.0.11", "uniffi", diff --git a/Cargo.toml b/Cargo.toml index d6d0c18..bdc5b47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,10 +19,13 @@ uniffi = { version = "0.28.3", features = ["build"] } [dependencies] uniffi = { version = "0.28.3" } thiserror = "2.0" +paste = "1.0.15" [dependencies.servicepoint] version = "0.15.0" features = ["all_compressions"] +git = "https://git.berlin.ccc.de/servicepoint/servicepoint.git" +branch = "next" [package.metadata.docs.rs] all-features = true diff --git a/src/brightness.rs b/src/brightness.rs index 0ea4591..8efba1e 100644 --- a/src/brightness.rs +++ b/src/brightness.rs @@ -1,3 +1,4 @@ +use crate::errors::ServicePointError; use crate::UniffiCustomTypeConverter; use servicepoint::Brightness; @@ -10,7 +11,8 @@ impl UniffiCustomTypeConverter for Brightness { where Self: Sized, { - Ok(Brightness::saturating_from(val)) + Ok(Brightness::try_from(val) + .map_err(|value| ServicePointError::InvalidBrightness { value })?) } fn from_custom(obj: Self) -> Self::Builtin { diff --git a/src/commands/bitmap.rs b/src/commands/bitmap.rs index 7cda71f..062f3dc 100644 --- a/src/commands/bitmap.rs +++ b/src/commands/bitmap.rs @@ -1,13 +1,14 @@ use crate::{ commands::wrap_command, compression_code::CompressionCode, - containers::bitmap::Bitmap, macros::wrap_object, + containers::bitmap::Bitmap, macros::wrap_object_attr_wrapped, }; use servicepoint::Origin; use std::sync::Arc; -wrap_object!(BitmapCommand); wrap_command!(BitmapCommand); +wrap_object_attr_wrapped!(BitmapCommand, bitmap, Bitmap); + #[uniffi::export] impl BitmapCommand { #[uniffi::constructor] diff --git a/src/commands/bitvec.rs b/src/commands/bitvec.rs index e449ea9..537f95f 100644 --- a/src/commands/bitvec.rs +++ b/src/commands/bitvec.rs @@ -1,11 +1,11 @@ use crate::{ commands::wrap_command, compression_code::CompressionCode, - containers::bitvec::BitVec, macros::wrap_object, + containers::bitvec::BitVec, macros::wrap_object_attr_wrapped, }; use std::sync::Arc; -wrap_object!(BitVecCommand); wrap_command!(BitVecCommand); +wrap_object_attr_wrapped!(BitVecCommand, bitvec, BitVec); #[uniffi::export] impl BitVecCommand { diff --git a/src/commands/brightness_grid.rs b/src/commands/brightness_grid.rs index 29c4e93..d48d8ee 100644 --- a/src/commands/brightness_grid.rs +++ b/src/commands/brightness_grid.rs @@ -1,12 +1,12 @@ use crate::{ commands::wrap_command, containers::brightness_grid::BrightnessGrid, - macros::wrap_object, + macros::wrap_object_attr_wrapped, }; use servicepoint::Origin; use std::sync::Arc; -wrap_object!(BrightnessGridCommand); wrap_command!(BrightnessGridCommand); +wrap_object_attr_wrapped!(BrightnessGridCommand, grid, BrightnessGrid); #[uniffi::export] impl BrightnessGridCommand { diff --git a/src/commands/cc_only.rs b/src/commands/cc_only.rs index b42a79e..576f1e2 100644 --- a/src/commands/cc_only.rs +++ b/src/commands/cc_only.rs @@ -1,10 +1,8 @@ -use crate::{commands::wrap_command, macros::wrap_object}; use std::sync::Arc; macro_rules! command_code_only_command { ($command_struct:ident) => { - wrap_object!($command_struct); - wrap_command!($command_struct); + crate::commands::wrap_command!($command_struct); #[uniffi::export] impl $command_struct { @@ -18,5 +16,4 @@ macro_rules! command_code_only_command { command_code_only_command!(ClearCommand); command_code_only_command!(HardResetCommand); -command_code_only_command!(BitmapLegacyCommand); command_code_only_command!(FadeOutCommand); diff --git a/src/commands/char_grid.rs b/src/commands/char_grid.rs index f02b93e..94da405 100644 --- a/src/commands/char_grid.rs +++ b/src/commands/char_grid.rs @@ -1,12 +1,12 @@ use crate::{ commands::wrap_command, containers::char_grid::CharGrid, - macros::wrap_object, + macros::wrap_object_attr_wrapped, }; use servicepoint::Origin; use std::sync::Arc; -wrap_object!(CharGridCommand); wrap_command!(CharGridCommand); +wrap_object_attr_wrapped!(CharGridCommand, grid, CharGrid); #[uniffi::export] impl CharGridCommand { diff --git a/src/commands/cp437.rs b/src/commands/cp437.rs index f4373c5..59d55a6 100644 --- a/src/commands/cp437.rs +++ b/src/commands/cp437.rs @@ -1,12 +1,12 @@ use crate::{ commands::wrap_command, containers::cp437_grid::Cp437Grid, - macros::wrap_object, + macros::wrap_object_attr_wrapped, }; use servicepoint::Origin; use std::sync::Arc; -wrap_object!(Cp437GridCommand); wrap_command!(Cp437GridCommand); +wrap_object_attr_wrapped!(Cp437GridCommand, grid, Cp437Grid); #[uniffi::export] impl Cp437GridCommand { diff --git a/src/commands/global_brightness.rs b/src/commands/global_brightness.rs index b95977e..5d626e5 100644 --- a/src/commands/global_brightness.rs +++ b/src/commands/global_brightness.rs @@ -1,8 +1,7 @@ -use crate::{commands::wrap_command, macros::wrap_object}; +use crate::commands::wrap_command; use servicepoint::Brightness; use std::sync::Arc; -wrap_object!(GlobalBrightnessCommand); wrap_command!(GlobalBrightnessCommand); #[uniffi::export] diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 224c26d..0773b18 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -8,35 +8,59 @@ mod global_brightness; use std::sync::Arc; -use crate::packet::Packet; pub use bitmap::BitmapCommand; pub use bitvec::{BinaryOperation, BitVecCommand}; pub use brightness_grid::BrightnessGridCommand; -pub use cc_only::{ - BitmapLegacyCommand, ClearCommand, FadeOutCommand, HardResetCommand, -}; +pub use cc_only::{ClearCommand, FadeOutCommand, HardResetCommand}; pub use char_grid::CharGridCommand; pub use cp437::Cp437GridCommand; #[uniffi::export] pub trait Command: Sync + Send { - fn as_packet(&self) -> Option>; + fn as_packet( + &self, + ) -> Result, crate::errors::ServicePointError>; } macro_rules! wrap_command { ($command:ident) => { - #[uniffi::export] - impl $command {} + crate::macros::wrap_object!($command); - impl crate::commands::Command for $command { - fn as_packet(&self) -> Option> { + #[uniffi::export] + impl $command { + fn as_packet( + &self, + ) -> Result< + Arc, + crate::errors::ServicePointError, + > { self.read() .clone() .try_into() .map(crate::packet::Packet::internal_new) - .ok() + .map_err(|_| { + crate::errors::ServicePointError::InvalidPacket + }) + } + + fn as_generic( + &self, + ) -> ::std::sync::Arc { + ::std::sync::Arc::new(self.clone()) + } + } + + impl crate::commands::Command for $command { + fn as_packet( + &self, + ) -> Result< + Arc, + crate::errors::ServicePointError, + > { + $command::as_packet(&self) } } }; } + pub(crate) use wrap_command; diff --git a/src/connection.rs b/src/connection.rs index 9f1f0e0..5b9ed52 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -1,3 +1,4 @@ +use crate::commands::Command; use crate::{errors::ServicePointError, packet::Packet}; use servicepoint::UdpSocketExt; use std::{net::UdpSocket, sync::Arc}; @@ -28,4 +29,11 @@ impl Connection { error: "send failed".to_string(), }) } + + pub fn send_command( + &self, + command: Arc, + ) -> Result<(), ServicePointError> { + self.send_packet(command.as_packet()?) + } } diff --git a/src/containers/bitmap.rs b/src/containers/bitmap.rs index 7598609..1418e5e 100644 --- a/src/containers/bitmap.rs +++ b/src/containers/bitmap.rs @@ -1,4 +1,7 @@ -use crate::macros::*; +use crate::{ + containers::{wrap_get_set_fill_2d, wrap_width_height}, + macros::wrap_object, +}; use servicepoint::Grid; use std::{ops::Deref, sync::Arc}; @@ -22,18 +25,13 @@ impl Bitmap { #[uniffi::constructor] pub fn load(width: u64, height: u64, data: Vec) -> Arc { + // TODO: throw exception Self::internal_new( servicepoint::Bitmap::load(width as usize, height as usize, &data) .unwrap(), ) } - pub fn equals(&self, other: &Bitmap) -> bool { - let a = self.read(); - let b = other.read(); - *a == *b - } - pub fn copy_raw(&self) -> Vec { self.read().deref().into() } diff --git a/src/containers/bitvec.rs b/src/containers/bitvec.rs index b5686d9..cd2c87d 100644 --- a/src/containers/bitvec.rs +++ b/src/containers/bitvec.rs @@ -18,31 +18,21 @@ impl BitVec { } pub fn set(&self, index: u64, value: bool) { - self.actual.write().unwrap().set(index as usize, value) + self.write().set(index as usize, value) } pub fn get(&self, index: u64) -> bool { - self.actual - .read() - .unwrap() - .get(index as usize) - .is_some_and(move |bit| *bit) + self.read().get(index as usize).is_some_and(move |bit| *bit) } pub fn fill(&self, value: bool) { - self.actual.write().unwrap().fill(value) + self.write().fill(value) } pub fn len(&self) -> u64 { self.read().len() as u64 } - pub fn equals(&self, other: &BitVec) -> bool { - let a = self.read(); - let b = other.read(); - *a == *b - } - pub fn copy_raw(&self) -> Vec { self.read().clone().into_vec() } diff --git a/src/containers/brightness_grid.rs b/src/containers/brightness_grid.rs index 6e99211..6bb3cbc 100644 --- a/src/containers/brightness_grid.rs +++ b/src/containers/brightness_grid.rs @@ -1,4 +1,7 @@ -use crate::macros::*; +use crate::{ + containers::{wrap_get_set_fill_2d, wrap_width_height}, + macros::wrap_object, +}; use servicepoint::{Brightness, Grid}; use std::{ops::Deref, sync::Arc}; @@ -28,12 +31,6 @@ impl BrightnessGrid { ) } - pub fn equals(&self, other: &BrightnessGrid) -> bool { - let a = self.read(); - let b = other.read(); - *a == *b - } - pub fn copy_raw(&self) -> Vec { self.read().deref().into() } diff --git a/src/containers/char_grid.rs b/src/containers/char_grid.rs index 2863282..387344c 100644 --- a/src/containers/char_grid.rs +++ b/src/containers/char_grid.rs @@ -1,4 +1,7 @@ -use crate::{containers::cp437_grid::Cp437Grid, macros::*}; +use crate::{ + containers::{cp437_grid::Cp437Grid, wrap_width_height}, + macros::wrap_object, +}; use servicepoint::{Grid, SetValueSeriesError}; use std::{convert::Into, sync::Arc}; @@ -37,58 +40,39 @@ impl CharGrid { value: String, ) -> Result<(), CharGridError> { let value = Self::str_to_char(value)?; - self.actual - .write() - .unwrap() - .set(x as usize, y as usize, value); + self.write().set(x as usize, y as usize, value); Ok(()) } pub fn get(&self, x: u64, y: u64) -> String { - self.actual - .read() - .unwrap() - .get(x as usize, y as usize) - .into() + self.read().get(x as usize, y as usize).into() } pub fn fill(&self, value: String) -> Result<(), CharGridError> { let value = Self::str_to_char(value)?; - self.actual.write().unwrap().fill(value); + self.write().fill(value); Ok(()) } - pub fn equals(&self, other: &CharGrid) -> bool { - let a = self.read(); - let b = other.read(); - *a == *b - } - pub fn as_string(&self) -> String { let grid = self.read(); String::from(&*grid) } pub fn set_row(&self, y: u64, row: String) -> Result<(), CharGridError> { - self.actual - .write() - .unwrap() + self.write() .set_row(y as usize, &row.chars().collect::>()) .map_err(CharGridError::from) } pub fn set_col(&self, x: u64, col: String) -> Result<(), CharGridError> { - self.actual - .write() - .unwrap() + self.write() .set_row(x as usize, &col.chars().collect::>()) .map_err(CharGridError::from) } pub fn get_row(&self, y: u64) -> Result { - self.actual - .read() - .unwrap() + self.read() .get_row(y as usize) .map(String::from_iter) .ok_or(CharGridError::OutOfBounds { @@ -98,9 +82,7 @@ impl CharGrid { } pub fn get_col(&self, x: u64) -> Result { - self.actual - .read() - .unwrap() + self.read() .get_col(x as usize) .map(String::from_iter) .ok_or(CharGridError::OutOfBounds { diff --git a/src/containers/cp437_grid.rs b/src/containers/cp437_grid.rs index bdcbfc1..fdd25e0 100644 --- a/src/containers/cp437_grid.rs +++ b/src/containers/cp437_grid.rs @@ -1,4 +1,8 @@ -use crate::{containers::char_grid::CharGrid, macros::*}; +use crate::{ + containers::char_grid::CharGrid, + containers::{wrap_get_set_fill_2d, wrap_width_height}, + macros::wrap_object, +}; use servicepoint::Grid; use std::{ops::Deref, sync::Arc}; @@ -28,12 +32,6 @@ impl Cp437Grid { ) } - pub fn equals(&self, other: &Cp437Grid) -> bool { - let a = self.read(); - let b = other.read(); - *a == *b - } - pub fn copy_raw(&self) -> Vec { self.read().deref().into() } diff --git a/src/containers/mod.rs b/src/containers/mod.rs index 7dce8bd..f1d4880 100644 --- a/src/containers/mod.rs +++ b/src/containers/mod.rs @@ -3,3 +3,44 @@ pub mod bitvec; pub mod brightness_grid; pub mod char_grid; pub mod cp437_grid; + +macro_rules! wrap_width_height { + ($t:ident) => { + #[uniffi::export] + impl $t { + pub fn width(&self) -> u64 { + self.read().width() as u64 + } + + pub fn height(&self) -> u64 { + self.read().height() as u64 + } + } + }; +} + +pub(crate) use wrap_width_height; + +macro_rules! wrap_get_set_fill_2d { + ($t:ident, $contained:ident) => { + #[uniffi::export] + impl $t { + pub fn set(&self, x: u64, y: u64, value: $contained) { + self.actual + .write() + .unwrap() + .set(x as usize, y as usize, value) + } + + pub fn get(&self, x: u64, y: u64) -> $contained { + self.read().get(x as usize, y as usize) + } + + pub fn fill(&self, value: $contained) { + self.actual.write().unwrap().fill(value) + } + } + }; +} + +pub(crate) use wrap_get_set_fill_2d; diff --git a/src/errors.rs b/src/errors.rs index 9c28f5f..03d05ba 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -4,4 +4,6 @@ pub enum ServicePointError { IoError { error: String }, #[error("The specified brightness value {value} is out of range")] InvalidBrightness { value: u8 }, + #[error("The provided packet is invalid or a conversion to packet failed")] + InvalidPacket, } diff --git a/src/macros.rs b/src/macros.rs index ee15b5e..140b42c 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,6 +1,7 @@ macro_rules! wrap_object { ($orig_t:ident, $new_t:ident) => { #[derive(uniffi::Object)] + #[uniffi::export(Debug, Eq, Hash)] pub struct $new_t { actual: ::std::sync::RwLock<::servicepoint::$orig_t>, } @@ -10,9 +11,15 @@ macro_rules! wrap_object { pub(crate) fn internal_new( actual: ::servicepoint::$orig_t, ) -> ::std::sync::Arc { - ::std::sync::Arc::new(Self { + ::std::sync::Arc::new(Self::internal_new_noarc(actual)) + } + + pub(crate) fn internal_new_noarc( + actual: ::servicepoint::$orig_t, + ) -> Self { + Self { actual: ::std::sync::RwLock::new(actual), - }) + } } pub(crate) fn read( @@ -37,51 +44,85 @@ macro_rules! wrap_object { Self::internal_new(other.read().clone()) } } + + impl Clone for $new_t { + fn clone(&self) -> Self { + Self::internal_new_noarc(self.read().clone()) + } + } + + impl ::std::fmt::Debug for $new_t { + fn fmt( + &self, + f: &mut ::std::fmt::Formatter, + ) -> Result<(), std::fmt::Error> { + f.write_fmt(format_args!("{:?}", self.read())) + } + } + + impl PartialEq for $new_t { + fn eq(&self, other: &$new_t) -> bool { + *self.read() == *other.read() + } + } + + impl Eq for $new_t {} + + impl ::std::hash::Hash for $new_t { + fn hash(&self, state: &mut H) { + ::std::hash::Hash::hash(&*self.read(), state) + } + } }; ($t:ident) => { - wrap_object!($t, $t); + crate::macros::wrap_object!($t, $t); }; } -pub(crate) use wrap_object; - -macro_rules! wrap_width_height { - ($t:ident) => { +macro_rules! wrap_object_attr_wrapped_get { + ($object:ident, $attr_name:ident, $attr_type:ident, $fun_name:ident) => { #[uniffi::export] - impl $t { - pub fn width(&self) -> u64 { - self.read().width() as u64 - } - - pub fn height(&self) -> u64 { - self.read().height() as u64 + impl $object { + pub fn $fun_name(&self) -> ::std::sync::Arc<$attr_type> { + $attr_type::internal_new(self.read().$attr_name.clone()) } } }; + ($object:ident, $attr_name:ident, $attr_type:ident) => { + paste::paste!{ + crate::macros::wrap_object_attr_wrapped_get!($object, $attr_name, $attr_type, []); + } + }; } -pub(crate) use wrap_width_height; - -macro_rules! wrap_get_set_fill_2d { - ($t:ident, $contained:ident) => { +macro_rules! wrap_object_attr_wrapped_set { + ($object:ident, $attr_name:ident, $attr_type:ident, $fun_name:ident) => { #[uniffi::export] - impl $t { - pub fn set(&self, x: u64, y: u64, value: $contained) { - self.actual - .write() - .unwrap() - .set(x as usize, y as usize, value) - } - - pub fn get(&self, x: u64, y: u64) -> $contained { - self.read().get(x as usize, y as usize) - } - - pub fn fill(&self, value: $contained) { - self.actual.write().unwrap().fill(value) + impl $object { + pub fn $fun_name(&self, $attr_name: ::std::sync::Arc<$attr_type>) { + self.write().$attr_name = $attr_name.read().clone(); } } }; + ($object:ident, $attr_name:ident, $attr_type:ident) => { + paste::paste!{ + crate::macros::wrap_object_attr_wrapped_set!($object, $attr_name, $attr_type, []); + } + }; } -pub(crate) use wrap_get_set_fill_2d; +macro_rules! wrap_object_attr_wrapped { + ($object:ident, $attr_name:ident, $attr_type:ident) => { + crate::macros::wrap_object_attr_wrapped_get!( + $object, $attr_name, $attr_type + ); + crate::macros::wrap_object_attr_wrapped_set!( + $object, $attr_name, $attr_type + ); + }; +} + +pub(crate) use { + wrap_object, wrap_object_attr_wrapped, wrap_object_attr_wrapped_get, + wrap_object_attr_wrapped_set, +}; diff --git a/src/packet.rs b/src/packet.rs index 5f769f1..ceaf052 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -1,12 +1,6 @@ -use crate::macros::wrap_object; -use std::sync::Arc; +use crate::macros::{wrap_object, wrap_object_attr_wrapped}; wrap_object!(Packet); wrap_object!(Header); -#[uniffi::export] -impl Packet { - pub fn get_header(&self) -> Arc
{ - Header::internal_new(self.read().header) - } -} +wrap_object_attr_wrapped!(Packet, header, Header);