diff options
author | Romeo Disca <romeo.disca@gmail.com> | 2020-08-08 04:24:43 +0200 |
---|---|---|
committer | Romeo Disca <romeo.disca@gmail.com> | 2020-08-08 12:08:08 +0200 |
commit | f2630bbb255dab230348dff8cedfcef40787e57e (patch) | |
tree | 5de013a9b7c0a1a0b822348e88b776cd6a911518 | |
parent | 36bfd7c66ad1332eaedecde3b6273f4db0559103 (diff) |
chore: bring everything together
-rw-r--r-- | Cargo.lock | 38 | ||||
-rw-r--r-- | Cargo.toml | 4 | ||||
-rw-r--r-- | src/client.rs | 66 | ||||
-rw-r--r--[-rwxr-xr-x] | src/commands/mod.rs (renamed from src/commands.rs) | 25 | ||||
-rw-r--r-- | src/commands/stream_mapper.rs | 192 | ||||
-rw-r--r--[-rwxr-xr-x] | src/enums.rs | 0 | ||||
-rw-r--r-- | src/events/mod.rs (renamed from src/events.rs) | 69 | ||||
-rw-r--r-- | src/events/stream_mapper.rs | 581 | ||||
-rw-r--r-- | src/main.rs | 10 |
9 files changed, 897 insertions, 88 deletions
@@ -1,6 +1,15 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] +name = "aho-corasick" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" +dependencies = [ + "memchr", +] + +[[package]] name = "arc-swap" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -265,6 +274,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] +name = "regex" +version = "1.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" + +[[package]] name = "serde" version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -284,7 +311,9 @@ dependencies = [ name = "simpleclient" version = "0.1.0" dependencies = [ + "bytes", "num_enum", + "regex", "tokio", ] @@ -318,6 +347,15 @@ dependencies = [ ] [[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] + +[[package]] name = "tokio" version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -7,5 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +bytes = "0.5" num_enum = "0.4.2" -tokio = { version = "0.2", features = ["full"] }
\ No newline at end of file +regex = "1" +tokio = { version = "0.2", features = ["full"] } diff --git a/src/client.rs b/src/client.rs index b6b681a..320ae86 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,22 +1,31 @@ -use std::sync::Arc; - use tokio::io::*; + +use bytes::BufMut; use tokio::net::TcpStream; -use super::events::*; +use std::net::Shutdown; +use std::sync::{Arc, Mutex, MutexGuard}; +use std::time::Duration; + +use super::events::Event; +use super::events::stream_mapper::*; +use super::commands::stream_mapper::CommandToByteMapper; use super::commands::Command; -pub fn event_handler<F>(f: F) -> Box<dyn FnMut(Event)> - where F: FnMut(Event) + 'static +type EventClosure = dyn FnMut(Event) + Sync + Send + 'static; +type EventClosureMutex = Box<EventClosure>; + +pub fn event_handler<F>(f: F) -> EventClosureMutex + where F: FnMut(Event) + Sync + Send + 'static { Box::new(f) } pub struct FlicClient { - reader: Arc<TcpStream>, - writer: Arc<TcpStream>, - map: Vec<Box<dyn FnMut(Event)>>, + stream: TcpStream, + command_mapper: CommandToByteMapper, + map: Vec<EventClosureMutex>, is_running: bool, } @@ -24,12 +33,10 @@ impl FlicClient { pub async fn new(conn: &str) -> Result<FlicClient> { match TcpStream::connect(conn).await { Ok(stream) => { - let reader = Arc::new(stream); - let writer = reader.clone(); - + println!("stream open"); Ok(FlicClient{ - reader, - writer, + stream, + command_mapper: CommandToByteMapper::new(), map: vec![], is_running: true, }) @@ -38,30 +45,41 @@ impl FlicClient { } } - pub fn register_event_handler(mut self, event: Box<dyn FnMut(Event)>) -> Self { + pub fn register_event_handler(mut self, event: EventClosureMutex) -> Self { self.map.push(event); self } pub async fn listen(&mut self) { + let mut mapper = ByteToEventMapper::new(); + let (mut reader, _writer) = self.stream.split(); + let mut buffer = vec![]; while self.is_running { - if let Some(mut r) = Arc::get_mut(&mut self.reader) { - if let Ok(value) = r.read_u8().await { - for ref mut f in self.map.as_mut_slice() { - f(Event::read_event(value, &mut r)); + if let Some(size) = reader.read_buf(&mut buffer).await.ok() { + for b in buffer.iter() { + match mapper.map(*b) { + EventResult::Some(Event::NoOp) => {} + EventResult::Some(event) => for ref mut f in &mut self.map { + f(event.clone()); + } + _ => {} } } + } } } - pub fn stop(&mut self) { + pub async fn stop(&mut self) { self.is_running = false; + self.stream.shutdown(Shutdown::Both); + println!("stopped"); } - pub fn submit(&mut self, cmd: Command) -> Result<()> { - if let Some(mut w) = Arc::get_mut(&mut self.writer) { - cmd.write_command(&mut w)?; - } - Ok(()) + pub async fn submit(&mut self, cmd: Command) { + let (_reader, mut writer) = self.stream.split(); + for b in self.command_mapper.map(cmd) { + writer.write_u8(b).await; + println!("{:?}", b); + } } } diff --git a/src/commands.rs b/src/commands/mod.rs index d84d9cd..e2b886b 100755..100644 --- a/src/commands.rs +++ b/src/commands/mod.rs @@ -1,8 +1,5 @@ -use num_enum::TryFromPrimitive; -use num_enum::IntoPrimitive; -use tokio::io::*; -use tokio::net::TcpStream; +pub mod stream_mapper; use super::enums::LatencyMode; @@ -76,24 +73,4 @@ impl Command { Self::RemoveBatteryStatusListener{..} => 13, } } - pub fn write_command(&self, writer: &mut TcpStream) -> Result<()> { - match self { - Self::GetInfo{..} => 0, - Self::CreateScanner{..} => 1, - Self::RemoveScanner{..} => 2, - Self::CreateConnectionChannel{..} => 3, - Self::RemoveConnectionChannel{..} => 4, - Self::ForceDisconnect{..} => 5, - Self::ChangeModeParameters{..} => 6, - Self::Ping{..} => 7, - Self::GetButtonInfo{..} => 8, - Self::CreateScanWizard{..} => 9, - Self::CancelScanWizard{..} => 10, - Self::DeleteButton{..} => 11, - Self::CreateBatteryStatusListener{..} => 12, - Self::RemoveBatteryStatusListener{..} => 13, - }; - - Ok(()) - } }
\ No newline at end of file diff --git a/src/commands/stream_mapper.rs b/src/commands/stream_mapper.rs new file mode 100644 index 0000000..2b6f612 --- /dev/null +++ b/src/commands/stream_mapper.rs @@ -0,0 +1,192 @@ +
+use regex::Regex;
+
+use std::collections::VecDeque;
+use std::collections::vec_deque::Drain;
+use std::convert::TryInto;
+
+use super::*;
+
+pub struct CommandToByteMapper {
+ buffer: VecDeque<u8>,
+}
+impl CommandToByteMapper {
+
+ pub fn new() -> CommandToByteMapper {
+ CommandToByteMapper{ buffer: VecDeque::new() }
+ }
+
+ pub fn map(&mut self, command: Command) -> Drain<u8> {
+ self.clear_buffer();
+
+ self.write_u8(command.opcode());
+ match command {
+ Command::GetInfo => {},
+ Command::CreateScanner {
+ scan_id,
+ } => {
+
+ self.write_u32(scan_id);
+ },
+ Command::RemoveScanner {
+ scan_id,
+ } => {
+
+ self.write_u32(scan_id);
+ },
+ Command::CreateConnectionChannel {
+ conn_id,
+ bd_addr,
+ latency_mode,
+ auto_disconnect_time,
+ } => {
+
+ self.write_u32(conn_id);
+ self.write_bdaddr(&bd_addr[..]);
+ if let Ok(latency_mode) = latency_mode.try_into() {
+ self.write_u8(latency_mode);
+ }
+ self.write_i16(auto_disconnect_time);
+ },
+ Command::RemoveConnectionChannel {
+ conn_id,
+ } => {
+
+ self.write_u32(conn_id);
+ },
+ Command::ForceDisconnect {
+ bd_addr,
+ } => {
+
+ self.write_bdaddr(&bd_addr[..]);
+ },
+ Command::ChangeModeParameters {
+ conn_id,
+ latency_mode,
+ auto_disconnect_time,
+ } => {
+
+ self.write_u32(conn_id);
+ if let Ok(latency_mode) = latency_mode.try_into() {
+ self.write_u8(latency_mode);
+ }
+ self.write_i16(auto_disconnect_time);
+ },
+ Command::Ping {
+ ping_id,
+ } => {
+
+ self.write_u32(ping_id);
+ },
+ Command::GetButtonInfo {
+ bd_addr,
+ } => {
+
+ self.write_bdaddr(&bd_addr[..]);
+ },
+ Command::CreateScanWizard {
+ scan_wizard_id,
+ } => {
+
+ self.write_u32(scan_wizard_id);
+ },
+ Command::CancelScanWizard {
+ scan_wizard_id,
+ } => {
+
+ self.write_u32(scan_wizard_id);
+ },
+ Command::DeleteButton {
+ bd_addr,
+ } => {
+
+ self.write_bdaddr(&bd_addr[..]);
+ },
+ Command::CreateBatteryStatusListener {
+ listener_id,
+ bd_addr,
+ } => {
+
+ self.write_u32(listener_id);
+ self.write_bdaddr(&bd_addr[..]);
+ },
+ Command::RemoveBatteryStatusListener {
+ listener_id,
+ } => {
+
+ self.write_u32(listener_id);
+ },
+ }
+
+ self.prepend_size();
+
+ self.buffer.drain(..)
+ }
+
+ fn clear_buffer(&mut self) {
+ self.buffer.drain(..);
+ }
+
+ fn prepend_size(&mut self) {
+ let len = self.buffer.len();
+ self.buffer.push_front((len >> 8) as u8);
+ self.buffer.push_front((len & 255) as u8);
+ }
+ fn write_u8(&mut self, value: u8) {
+ let mut buf = vec![value].into_iter().collect();
+ self.buffer.append(&mut buf);
+ }
+/*
+ fn write_bool(&mut self, value: bool) {
+ if value {
+ self.write_u8(1);
+ }
+ else {
+ self.write_u8(0);
+ }
+ }
+*/
+ fn write_u16(&mut self, value: u16) {
+ self.write_u8(value as u8);
+ self.write_u8((value >> 8) as u8);
+ }
+ fn write_i16(&mut self, value: i16) {
+ self.write_u8(value as u8);
+ self.write_u8((value >> 8) as u8);
+ }
+ fn write_u32(&mut self, value: u32) {
+ self.write_u16(value as u16);
+ self.write_u16((value >> 16) as u16);
+ }
+/*
+ fn write_i32(&mut self, value: i32) {
+ self.write_i16(value as i16);
+ self.write_i16((value >> 16) as i16);
+ }
+*/
+ fn write_bdaddr(&mut self, str: &str) {
+ let re = Regex::new(r"([0-9a-z]{2}:){5}[0-9a-z]{2}").unwrap();
+ if re.is_match(str) {
+ if let Some(b) = hex_to_u8(&str[15..]) { self.write_u8(b); }
+ if let Some(b) = hex_to_u8(&str[12..]) { self.write_u8(b); }
+ if let Some(b) = hex_to_u8(&str[ 9..]) { self.write_u8(b); }
+ if let Some(b) = hex_to_u8(&str[ 6..]) { self.write_u8(b); }
+ if let Some(b) = hex_to_u8(&str[ 3..]) { self.write_u8(b); }
+ if let Some(b) = hex_to_u8(&str[ ..]) { self.write_u8(b); }
+ }
+ }
+
+}
+
+fn hex_to_u8(buffer: &str) -> Option<u8> {
+ let mut char_indices = buffer.char_indices();
+ match (char_indices.next(), char_indices.next()) {
+ (Some((0, upper)), Some((1, lower))) => {
+ let mut b = 0u8;
+ if let Some(v) = upper.to_digit(16) { b += (v as u8) << 4; }
+ if let Some(v) = lower.to_digit(16) { b += v as u8; }
+ Some(b)
+ },
+ _ => None,
+ }
+}
diff --git a/src/enums.rs b/src/enums.rs index 64284a2..64284a2 100755..100644 --- a/src/enums.rs +++ b/src/enums.rs diff --git a/src/events.rs b/src/events/mod.rs index ac00472..0cfd910 100644 --- a/src/events.rs +++ b/src/events/mod.rs @@ -1,11 +1,41 @@ -use tokio::net::TcpStream; +pub mod stream_mapper; + +use num_enum::TryFromPrimitive; +use num_enum::IntoPrimitive; use super::enums::*; +#[repr(u8)] +#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, IntoPrimitive, TryFromPrimitive)] +pub enum OpCode { + AdvertisementPacket = 0, + CreateConnectionChannelResponse = 1, + ConnectionStatusChanged = 2, + ConnectionChannelRemoved = 3, + ButtonUpOrDown = 4, + ButtonClickOrHold = 5, + ButtonSingleOrDoubleClick = 6, + ButtonSingleOrDoubleClickOrHold = 7, + NewVerifiedButton = 8, + GetInfoResponse = 9, + NoSpaceForNewConnection = 10, + GotSpaceForNewConnection = 11, + BluetoothControllerStateChange = 12, + PingResponse = 13, + GetButtonInfoResponse = 14, + ScanWizardFoundPrivateButton = 15, + ScanWizardFoundPublicButton = 16, + ScanWizardButtonConnected = 17, + ScanWizardCompleted = 18, + ButtonDeleted = 19, + BatteryStatus = 20, +} + #[allow(dead_code)] #[derive(Debug, PartialEq, Eq, Clone)] pub enum Event { + NoOp, CorruptEvent, AdvertisementPacket { @@ -130,40 +160,3 @@ pub enum Event { }, } -impl Event { - pub fn opcode(&self) -> u8 { - match self { - Self::CorruptEvent => 255, - Self::AdvertisementPacket{..} => 0, - Self::CreateConnectionChannelResponse{..} => 1, - Self::ConnectionStatusChanged{..} => 2, - Self::ConnectionChannelRemoved{..} => 3, - Self::ButtonUpOrDown{..} => 4, - Self::ButtonClickOrHold{..} => 5, - Self::ButtonSingleOrDoubleClick{..} => 6, - Self::ButtonSingleOrDoubleClickOrHold{..} => 7, - Self::NewVerifiedButton{..} => 8, - Self::GetInfoResponse{..} => 9, - Self::NoSpaceForNewConnection{..} => 10, - Self::GotSpaceForNewConnection{..} => 11, - Self::BluetoothControllerStateChange{..} => 12, - Self::PingResponse{..} => 13, - Self::GetButtonInfoResponse{..} => 14, - Self::ScanWizardFoundPrivateButton{..} => 15, - Self::ScanWizardFoundPublicButton{..} => 16, - Self::ScanWizardButtonConnected{..} => 17, - Self::ScanWizardCompleted{..} => 18, - Self::ButtonDeleted{..} => 19, - Self::BatteryStatus{..} => 20, - } - } - - pub fn read_event(opcode: u8, reader: &mut TcpStream) -> Event { - match opcode { - 13 => Self::PingResponse{ping_id: 8}, - _ => Self::CorruptEvent, - } - } - -} - diff --git a/src/events/stream_mapper.rs b/src/events/stream_mapper.rs new file mode 100644 index 0000000..5244d58 --- /dev/null +++ b/src/events/stream_mapper.rs @@ -0,0 +1,581 @@ + +use super::*; +use std::collections::VecDeque; +use std::usize; +use std::convert::TryInto; +use std::convert::TryFrom; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum EventResult { + Some(Event), + None, + Failure(Event), + Drained, + Pending, + CorruptPackage, +} +enum HasPacketResult { + Yes, + NotYet, + Failure(Event), +} + +pub struct ByteToEventMapper { + fifo: VecDeque<u8>, +} +impl ByteToEventMapper { + + pub fn new() -> ByteToEventMapper { + ByteToEventMapper { fifo: VecDeque::new() } + } + + pub fn map(&mut self, value: u8) -> EventResult { + + self.fifo.push_back(value); + + match self.fifo.len() { + 0..=2 => EventResult::None, + 3..=2047 => match self.has_packet() { + HasPacketResult::NotYet => EventResult::Pending, + HasPacketResult::Failure(event) => { + self.fifo.drain(..); + EventResult::Failure(event) + }, + HasPacketResult::Yes => match self.read_event() { + Event::CorruptEvent => EventResult::CorruptPackage, + event => EventResult::Some(event), + } + }, + _ => { + self.fifo.drain(..); + EventResult::Drained + }, + } + + } + + pub fn has_packet(&self) -> HasPacketResult { + match (self.fifo.get(0), self.fifo.get(1), self.fifo.get(2)) { + (Some(&lower), Some(&upper), Some(&opcode)) => { + let len = ((upper as usize) << 8) + (lower as usize); + if OpCode::try_from(opcode).is_err() { + HasPacketResult::Failure(Event::CorruptEvent) + } + else if self.fifo.len() >= len + 2 { + HasPacketResult::Yes + } + else { + HasPacketResult::NotYet + } + }, + _ => HasPacketResult::NotYet, + } + } + fn read_u8(&mut self) -> Option<u8> { + if self.fifo.is_empty() { return None; } + let mut iter = self.fifo.drain(..1); + iter.next() + } + fn read_u16(&mut self) -> Option<u16> { + match (self.read_u8(), self.read_u8()) { + (Some(lower), Some(higher)) => Some((lower as u16) | (higher as u16) << 8), + _ => None, + } + } + fn read_u32(&mut self) -> Option<u32> { + match (self.read_u16(), self.read_u16()) { + (Some(lower), Some(higher)) => Some((lower as u32) | (higher as u32) << 16), + _ => None, + } + } + fn read_u64(&mut self) -> Option<u64> { + match (self.read_u32(), self.read_u32()) { + (Some(lower), Some(higher)) => Some((lower as u64) | (higher as u64) << 32), + _ => None, + } + } + fn read_i8(&mut self) -> Option<i8> { + match self.read_u8() { + Some(expr) => Some(expr as i8), + None => None, + } + } + fn read_i16(&mut self) -> Option<i16> { + match self.read_u16() { + Some(expr) => Some(expr as i16), + None => None, + } + } + fn read_i32(&mut self) -> Option<i32> { + match self.read_u32() { + Some(expr) => Some(expr as i32), + None => None, + } + } + fn read_bool(&mut self) -> Option<bool> { + match self.read_u8() { + Some(expr) => Some(expr != 0), + None => None, + } + } + fn read_enum(&mut self) -> Option<u8> { + self.read_u8() + } + fn read_bdaddr(&mut self) -> Option<String> { + let mut out = String::new(); + let mut buffer = vec![self.read_u8(), self.read_u8(), self.read_u8(), self.read_u8(), self.read_u8(), self.read_u8()]; + buffer.reverse(); + for (idx, b) in buffer.iter().enumerate() { + if idx > 0 { out.push(':'); } + match b { + Some(v) => { + out.push(u8_to_hex(v >> 4)); + out.push(u8_to_hex(v << 4 >> 4)); + }, + None => return None, + } + } + Some(out) + } + fn read_string(&mut self) -> Option<String> { + match self.read_u8() { + Some(len) => { + let mut buf = vec![]; + for _ in 0..len { + match self.read_u8() { + Some(b) => buf.push(b), + _ => return None, + } + } + String::from_utf8(buf).ok() + }, + _ => None, + } + } + fn read_uuid(&mut self) -> Option<String> { + let mut out = String::new(); + for _ in 0..16 { + let b = self.read_u8(); + match b { + Some(v) => { + out.push(u8_to_hex(v >> 4)); + out.push(u8_to_hex(v << 4 >> 4)); + }, + None => return None, + } + } + if &out[..] == "00000000000000000000000000000000" { + None + } + else { + Some(out) + } + } + + fn read_event(&mut self) -> Event { + self.read_u8(); + self.read_u8(); + match self.read_u8() { + Some(opcode) => match opcode.try_into().ok() { + + Some(OpCode::AdvertisementPacket) => match ( + self.read_u32(), + self.read_bdaddr(), + self.read_string(), + self.read_u8(), + self.read_bool(), + self.read_bool(), + self.read_bool(), + self.read_bool(), + ) { + ( + Some(scan_id), + Some(bd_addr), + Some(name), + Some(rssi), + Some(is_private), + Some(already_verified), + Some(already_connected_to_this_device), + Some(already_connected_to_other_device), + ) => Event::AdvertisementPacket { + scan_id, + bd_addr, + name, + rssi, + is_private, + already_verified, + already_connected_to_this_device, + already_connected_to_other_device, + }, + _ => Event::CorruptEvent, + }, + Some(OpCode::CreateConnectionChannelResponse) => match ( + self.read_u32(), + self.read_enum(), + self.read_enum(), + ) { + ( + Some(conn_id), + Some(error), + Some(connection_status), + + ) => + match (error.try_into().ok(), connection_status.try_into().ok()) { + (Some(error), Some(connection_status)) => + Event::CreateConnectionChannelResponse { + conn_id, + error, + connection_status, + }, + _ => Event::CorruptEvent, + }, + _ => Event::CorruptEvent, + }, + Some(OpCode::ConnectionStatusChanged) => match ( + self.read_u32(), + self.read_enum(), + self.read_enum(), + ) { + ( + Some(conn_id), + Some(connection_status), + Some(disconnect_reason), + ) => + match (connection_status.try_into().ok(), disconnect_reason.try_into().ok()) { + (Some(connection_status), Some(disconnect_reason)) => + Event::ConnectionStatusChanged { + conn_id, + connection_status, + disconnect_reason, + }, + _ => Event::CorruptEvent, + }, + _ => Event::CorruptEvent, + }, + Some(OpCode::ConnectionChannelRemoved) => match ( + self.read_u32(), + self.read_enum(), + ) { + ( + Some(conn_id), + Some(removed_reason), + ) => + match removed_reason.try_into().ok() { + Some(removed_reason) => + Event::ConnectionChannelRemoved { + conn_id, + removed_reason, + }, + _ => Event::CorruptEvent, + }, + _ => Event::CorruptEvent, + }, + Some(OpCode::ButtonUpOrDown) => match ( + self.read_u32(), + self.read_enum(), + self.read_bool(), + self.read_i32(), + ) { + ( + Some(conn_id), + Some(click_type), + Some(was_queued), + Some(time_diff), + ) => + match click_type.try_into().ok() { + Some(click_type) => + Event::ButtonUpOrDown { + conn_id, + click_type, + was_queued, + time_diff, + }, + _ => Event::CorruptEvent, + } + _ => Event::CorruptEvent, + }, + Some(OpCode::ButtonClickOrHold) => match ( + self.read_u32(), + self.read_enum(), + self.read_bool(), + self.read_i32(), + ) { + ( + Some(conn_id), + Some(click_type), + Some(was_queued), + Some(time_diff), + ) => + match click_type.try_into().ok() { + Some(click_type) => + Event::ButtonClickOrHold { + conn_id, + click_type, + was_queued, + time_diff, + }, + _ => Event::CorruptEvent, + } + _ => Event::CorruptEvent, + }, + Some(OpCode::ButtonSingleOrDoubleClick) => match ( + self.read_u32(), + self.read_enum(), + self.read_bool(), + self.read_i32(), + ) { + ( + Some(conn_id), + Some(click_type), + Some(was_queued), + Some(time_diff), + ) => + match click_type.try_into().ok() { + Some(click_type) => + Event::ButtonSingleOrDoubleClick { + conn_id, + click_type, + was_queued, + time_diff, + }, + _ => Event::CorruptEvent, + } + _ => Event::CorruptEvent, + }, + Some(OpCode::ButtonSingleOrDoubleClickOrHold) => match ( + self.read_u32(), + self.read_enum(), + self.read_bool(), + self.read_i32(), + ) { + ( + Some(conn_id), + Some(click_type), + Some(was_queued), + Some(time_diff), + ) => + match click_type.try_into().ok() { + Some(click_type) => + Event::ButtonSingleOrDoubleClickOrHold { + conn_id, + click_type, + was_queued, + time_diff, + }, + _ => Event::CorruptEvent, + } + _ => Event::CorruptEvent, + }, + Some(OpCode::NewVerifiedButton) => match ( + self.read_bdaddr(), + ) { + ( + Some(bd_addr), + ) => Event::NewVerifiedButton { + bd_addr, + }, + _ => Event::CorruptEvent, + }, + Some(OpCode::GetInfoResponse) => match ( + self.read_enum(), + self.read_bdaddr(), + self.read_enum(), + self.read_u8(), + self.read_i16(), + self.read_u8(), + self.read_bool(), + self.read_u16(), + ) { + ( + Some(bluetooth_controller_state), + Some(my_bd_addr), + Some(my_bd_addr_type), + Some(max_pending_connections), + Some(max_concurrently_connected_buttons), + Some(current_pending_connections), + Some(currently_no_space_for_new_connection), + Some(buttons_size), + ) => { + let mut bd_addr_of_verified_buttons = vec![]; + for _ in 0..buttons_size { + match self.read_bdaddr() { + Some(bd_addr) => bd_addr_of_verified_buttons.push(bd_addr), + None => return Event::CorruptEvent, + } + } + match (bluetooth_controller_state.try_into().ok(), my_bd_addr_type.try_into().ok()) { + (Some(bluetooth_controller_state), Some(my_bd_addr_type)) => + Event::GetInfoResponse { + bluetooth_controller_state, + my_bd_addr, + my_bd_addr_type, + max_pending_connections, + max_concurrently_connected_buttons, + current_pending_connections, + currently_no_space_for_new_connection, + bd_addr_of_verified_buttons, + }, + _ => Event::CorruptEvent, + } + }, + _ => Event::CorruptEvent, + }, + Some(OpCode::NoSpaceForNewConnection) => match ( + self.read_u8(), + ) { + ( + Some(max_concurrently_connected_buttons), + ) => Event::NoSpaceForNewConnection { + max_concurrently_connected_buttons, + }, + _ => Event::CorruptEvent, + }, + Some(OpCode::GotSpaceForNewConnection) => match ( + self.read_u8(), + ) { + ( + Some(max_concurrently_connected_buttons), + ) => Event::GotSpaceForNewConnection { + max_concurrently_connected_buttons, + }, + _ => Event::CorruptEvent, + }, + Some(OpCode::BluetoothControllerStateChange) => match ( + self.read_enum(), + ) { + ( + Some(state), + ) => + match state.try_into().ok() { + Some(state) => + Event::BluetoothControllerStateChange { + state, + }, + _ => Event::CorruptEvent, + } + _ => Event::CorruptEvent, + }, + Some(OpCode::PingResponse) => match ( + self.read_u32(), + ) { + ( + Some(ping_id), + ) => Event::PingResponse { + ping_id, + }, + _ => Event::CorruptEvent, + }, + Some(OpCode::GetButtonInfoResponse) => match ( + self.read_bdaddr(), + self.read_uuid(), + self.read_string(), + self.read_string(), + ) { + ( + Some(bd_addr), + Some(uuid), + color, + serial_number, + ) => Event::GetButtonInfoResponse { + bd_addr, + uuid, + color, + serial_number, + }, + _ => Event::CorruptEvent, + }, + Some(OpCode::ScanWizardFoundPrivateButton) => match ( + self.read_u32(), + ) { + ( + Some(scan_wizard_id), + ) => Event::ScanWizardFoundPrivateButton { + scan_wizard_id, + }, + _ => Event::CorruptEvent, + }, + Some(OpCode::ScanWizardButtonConnected) => match ( + self.read_u32(), + ) { + ( + Some(scan_wizard_id), + ) => Event::ScanWizardButtonConnected { + scan_wizard_id, + }, + _ => Event::CorruptEvent, + }, + Some(OpCode::ScanWizardFoundPublicButton) => match ( + self.read_u32(), + self.read_bdaddr(), + self.read_string(), + ) { + ( + Some(scan_wizard_id), + Some(bd_addr), + Some(name), + ) => Event::ScanWizardFoundPublicButton { + scan_wizard_id, + bd_addr, + name, + }, + _ => Event::CorruptEvent, + }, |