diff --git a/atem-connection-rs/src/atem_lib/atem_field.rs b/atem-connection-rs/src/atem_lib/atem_field.rs index 1fa07d7..3ea7a22 100644 --- a/atem-connection-rs/src/atem_lib/atem_field.rs +++ b/atem-connection-rs/src/atem_lib/atem_field.rs @@ -2,7 +2,7 @@ /// An uninterpreted ATEM protocol field - a 4-ascii character type plus variable length data pub struct RawField<'a> { - pub r#type: &'a [u8; 4], + pub r#type: &'a str, pub data: &'a [u8], } @@ -13,7 +13,7 @@ pub struct _Ver { } impl<'a> Field<'a> for _Ver { - const TYPE: [u8; 4] = [b'_', b'v', b'e', b'r']; + const TYPE: &'static str = "_ver"; fn decode(data: &'a [u8]) -> Result { let data = checked_len::<4>(data)?; @@ -32,7 +32,7 @@ pub struct PrvI { } impl<'a> Field<'a> for PrvI { - const TYPE: [u8; 4] = [b'P', b'r', b'v', b'I']; + const TYPE: &'static str = "PrvI"; fn decode(data: &'a [u8]) -> Result { let data = checked_len::<8>(data)?; Ok(Self { @@ -50,7 +50,7 @@ pub struct PrgI { } impl<'a> Field<'a> for PrgI { - const TYPE: [u8; 4] = [b'P', b'r', b'g', b'I']; + const TYPE: &'static str = "PrgI"; fn decode(data: &'a [u8]) -> Result { let data = checked_len::<4>(data)?; Ok(Self { @@ -61,10 +61,10 @@ impl<'a> Field<'a> for PrgI { } pub trait Field<'a>: Sized { - const TYPE: [u8; 4]; + const TYPE: &'static str; fn decode(data: &'a [u8]) -> Result; fn try_from_raw(raw: RawField<'a>) -> Result { - if Self::TYPE != *raw.r#type { + if Self::TYPE != raw.r#type { Err(FieldParsingError::MismatchedFieldType) } else { Self::decode(&raw.data) @@ -75,7 +75,6 @@ pub trait Field<'a>: Sized { #[derive(Debug)] pub enum FieldParsingError { UnexpectedLength { expected: usize, got: usize }, - UnknownFieldType { r#type: [u8; 4] }, MismatchedFieldType, } diff --git a/atem-connection-rs/src/atem_lib/atem_packet.rs b/atem-connection-rs/src/atem_lib/atem_packet.rs index 2d3cfa8..832ef11 100755 --- a/atem-connection-rs/src/atem_lib/atem_packet.rs +++ b/atem-connection-rs/src/atem_lib/atem_packet.rs @@ -1,5 +1,7 @@ use core::{fmt::Display, str}; +use super::atem_field::{Field, FieldParsingError, RawField}; + /// The "hello" packet to start communication with the ATEM pub const COMMAND_CONNECT_HELLO: [u8; 20] = [ 0x10, 0x14, 0x53, 0xab, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, @@ -11,9 +13,10 @@ pub struct AtemPacket> { buf: T, } -impl<'a> From<&'a [u8]> for AtemPacket<&'a [u8]> { - fn from(buf: &'a [u8]) -> Self { - AtemPacket { buf } +impl<'a> TryFrom<&'a [u8]> for AtemPacket<&'a [u8]> { + type Error = AtemPacketErr; + fn try_from(buf: &'a [u8]) -> Result { + AtemPacket::new_checked(buf) } } @@ -97,10 +100,7 @@ impl> AtemPacket { pub fn ack_reply(&self) -> Option { self.has_flag(PacketFlag::AckReply) - .then_some(u16::from_be_bytes([ - self.buf.as_ref()[4], - self.buf.as_ref()[5], - ])) + .then_some(self.ack_number()) } /// Return true if this packet has the given [`PacketFlag`] @@ -111,10 +111,19 @@ impl> AtemPacket { /// Get an iterator over the `Field`s in this packet. /// /// Returns None if this is a packet without fields. - pub fn fields(&self) -> Fields { + pub fn raw_fields(&self) -> RawFields { // TODO: do we only ever get newsessionid during the handshake (i.e. not in a packet with fields)? let has_fields = !self.has_flag(PacketFlag::NewSessionId); - Fields::new(if has_fields { self.body() } else { &[] }) + RawFields::new(if has_fields { self.body() } else { &[] }) + } + + pub fn fields<'a, F: Field<'a>>( + &'a self, + ) -> impl Iterator> + use<'a, F, T> { + self.raw_fields().filter_map(|f| match F::try_from_raw(f) { + Err(FieldParsingError::MismatchedFieldType) => None, + x => Some(x), + }) } pub fn body(&self) -> &[u8] { @@ -137,27 +146,21 @@ impl> Display for AtemPacket { } } -/// An ATEM protocol field - a 4-ascii character type plus variable length data -pub struct Field<'a> { - pub r#type: &'a str, - pub data: &'a [u8], -} - -/// Created by [`AtemPacket::fields`] -pub struct Fields<'a> { +/// Created by [`AtemPacket::raw_fields`] +pub struct RawFields<'a> { data: &'a [u8], // The offset of the next field in the packet offset: usize, } -impl<'a> Fields<'a> { +impl<'a> RawFields<'a> { pub(crate) fn new(data: &'a [u8]) -> Self { Self { data, offset: 0 } } } -impl<'a> Iterator for Fields<'a> { - type Item = Field<'a>; +impl<'a> Iterator for RawFields<'a> { + type Item = RawField<'a>; fn next(&mut self) -> Option { let remain = self.data.len() - self.offset; if remain == 0 { @@ -172,7 +175,7 @@ impl<'a> Iterator for Fields<'a> { // TODO: sanity check length let r#type = str::from_utf8(&self.data[self.offset + 4..=self.offset + 7]).unwrap(); let data = &self.data[self.offset + 8..self.offset + (length as usize)]; - self.offset += (length as usize); - Some(Field { r#type, data }) + self.offset += length as usize; + Some(RawField { r#type, data }) } } diff --git a/atem-connection-rs/src/atem_lib/atem_socket.rs b/atem-connection-rs/src/atem_lib/atem_socket.rs index e96529b..a006a77 100644 --- a/atem-connection-rs/src/atem_lib/atem_socket.rs +++ b/atem-connection-rs/src/atem_lib/atem_socket.rs @@ -394,7 +394,7 @@ impl AtemSocket { log::debug!("Received {}", atem_packet,); log::debug!( "fields: {}", - atem_packet.fields().map(|f| f.r#type).join(",") + atem_packet.raw_fields().map(|f| f.r#type).join(",") ); self.last_received_at = SystemTime::now(); diff --git a/atem-connection-rs/src/atem_lib/mod.rs b/atem-connection-rs/src/atem_lib/mod.rs index 388a4e1..9e604ce 100644 --- a/atem-connection-rs/src/atem_lib/mod.rs +++ b/atem-connection-rs/src/atem_lib/mod.rs @@ -1,2 +1,3 @@ +pub mod atem_field; pub mod atem_packet; pub mod atem_socket; diff --git a/atem-connection-rs/src/commands/parse_commands.rs b/atem-connection-rs/src/commands/parse_commands.rs index e32386e..a463c0e 100644 --- a/atem-connection-rs/src/commands/parse_commands.rs +++ b/atem-connection-rs/src/commands/parse_commands.rs @@ -1,7 +1,7 @@ use std::{collections::VecDeque, sync::Arc}; use crate::{ - atem_lib::atem_packet::{AtemPacket, Fields}, + atem_lib::atem_packet::{AtemPacket, RawFields}, commands::device_profile::version::{deserialize_version, DESERIALIZE_VERSION_RAW_NAME}, enums::ProtocolVersion, }; @@ -32,7 +32,7 @@ pub fn deserialize_commands>( ) -> VecDeque> { let mut parsed_commands: VecDeque> = VecDeque::new(); - for field in Fields::new(payload.as_ref()) { + for field in RawFields::new(payload.as_ref()) { let name: &str = field.r#type.try_into().unwrap(); log::debug!("Received command {} with length {}", name, field.data.len(),);