From eef5d0e86423b18ade0eba8861a56a315913929d Mon Sep 17 00:00:00 2001 From: Maxime Borges Date: Mon, 26 Sep 2022 19:15:18 +0200 Subject: [PATCH 1/3] Add PacketOwned variants This allows to copy parsed packets into data-owned packets that can be moved to other threads etc. --- README.md | 12 ++- ublox/src/ubx_packets.rs | 10 +- ublox/src/ubx_packets/packets.rs | 98 +++++++++---------- ublox_derive/src/output.rs | 162 ++++++++++++++++++++++++++----- 4 files changed, 205 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index 8c1ebed..d518002 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Constructing Packets ==================== Constructing packets happens using the `Builder` variant of the packet, for example: -``` +```rust use ublox::{CfgPrtUartBuilder, UartPortId, UartMode, DataBits, Parity, StopBits, InProtoMask, OutProtoMask}; let packet: [u8; 28] = CfgPrtUartBuilder { portid: UartPortId::Uart1, @@ -36,7 +36,7 @@ let packet: [u8; 28] = CfgPrtUartBuilder { ``` For variable-size packet like `CfgValSet`, you can construct it into a new `Vec`: -``` +```rust use ublox::{cfg_val::CfgVal::*, CfgLayer, CfgValSetBuilder}; let packet_vec: Vec = CfgValSetBuilder { version: 1, @@ -48,7 +48,7 @@ let packet_vec: Vec = CfgValSetBuilder { let packet: &[u8] = packet_vec.as_slice(); ``` Or by extending to an existing one: -``` +```rust let mut packet_vec = Vec::new(); CfgValSetBuilder { version: 1, @@ -65,7 +65,7 @@ Parsing Packets =============== Parsing packets happens by instantiating a `Parser` object and then adding data into it using its `consume()` method. The parser contains an internal buffer of data, and when `consume()` is called that data is copied into the internal buffer and an iterator-like object is returned to access the packets. For example: -``` +```rust use ublox::Parser; let mut parser = Parser::default(); let my_raw_data = vec![1, 2, 3, 4]; // From your serial port @@ -73,7 +73,9 @@ let mut it = parser.consume(&my_raw_data); loop { match it.next() { Some(Ok(packet)) => { - // We've received a &PacketRef, we can handle it + // We've received a &PacketRef, we can handle it here + // Or we can convert it to an owned structure, so we can move it + let owned_packet = packet.to_owned(); } Some(Err(_)) => { // Received a malformed packet diff --git a/ublox/src/ubx_packets.rs b/ublox/src/ubx_packets.rs index 7cb7127..8496d7c 100644 --- a/ublox/src/ubx_packets.rs +++ b/ublox/src/ubx_packets.rs @@ -83,13 +83,21 @@ pub trait UbxPacketCreator { } /// Packet not supported yet by this crate -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] pub struct UbxUnknownPacketRef<'a> { pub payload: &'a [u8], pub class: u8, pub msg_id: u8, } +/// Packet not supported yet by this crate +#[derive(Clone, Debug)] +pub struct UbxUnknownPacketOwned { + pub payload: Vec, + pub class: u8, + pub msg_id: u8, +} + /// Request specific packet pub struct UbxPacketRequest { req_class: u8, diff --git a/ublox/src/ubx_packets/packets.rs b/ublox/src/ubx_packets/packets.rs index 9233fa8..ccda848 100644 --- a/ublox/src/ubx_packets/packets.rs +++ b/ublox/src/ubx_packets/packets.rs @@ -1,6 +1,6 @@ use super::{ ubx_checksum, MemWriter, Position, UbxChecksumCalc, UbxPacketCreator, UbxPacketMeta, - UbxUnknownPacketRef, SYNC_CHAR_1, SYNC_CHAR_2, + UbxUnknownPacketOwned, UbxUnknownPacketRef, SYNC_CHAR_1, SYNC_CHAR_2, }; use crate::cfg_val::CfgVal; use crate::error::{MemWriterError, ParserError}; @@ -140,11 +140,11 @@ struct NavHpPosLlh { /// Horizontal accuracy estimate (mm) #[ubx(map_type = f64, scale = 1e-1)] - horizontal_accuracy: u32, + horiz_accuracy: u32, /// Vertical accuracy estimate (mm) #[ubx(map_type = f64, scale = 1e-1)] - vertical_accuracy: u32, + vert_accuracy: u32, } /// Navigation Position Velocity Time Solution @@ -802,7 +802,7 @@ impl Default for OdoProfile { } } -/// Configure Jamming interference monitoring +/// Configure Jamming interference monitoring #[ubx_packet_recv_send] #[ubx(class = 0x06, id = 0x39, fixed_payload_len = 8)] struct CfgItfm { @@ -843,7 +843,7 @@ impl Default for CfgItfmConfig { impl CfgItfmConfig { pub fn new(enable: bool, bb_threshold: u32, cw_threshold: u32) -> Self { Self { - enable, + enable, bb_threshold: bb_threshold.into(), cw_threshold: cw_threshold.into(), algorithm_bits: CfgItfmAlgoBits::default(), @@ -851,10 +851,10 @@ impl CfgItfmConfig { } const fn into_raw(self) -> u32 { - (self.enable as u32)<<31 - | self.cw_threshold.into_raw() - | self.bb_threshold.into_raw() - | self.algorithm_bits.into_raw() + (self.enable as u32) << 31 + | self.cw_threshold.into_raw() + | self.bb_threshold.into_raw() + | self.algorithm_bits.into_raw() } } @@ -880,7 +880,7 @@ pub struct CfgItfmBbThreshold(u32); impl CfgItfmBbThreshold { const POSITION: u32 = 0; const LENGTH: u32 = 4; - const MASK: u32 = (1< u32 { (self.0 & Self::MASK) << Self::POSITION } @@ -905,7 +905,7 @@ pub struct CfgItfmCwThreshold(u32); impl CfgItfmCwThreshold { const POSITION: u32 = 4; const LENGTH: u32 = 5; - const MASK: u32 = (1< u32 { (self.0 & Self::MASK) << Self::POSITION } @@ -930,7 +930,7 @@ pub struct CfgItfmAlgoBits(u32); impl CfgItfmAlgoBits { const POSITION: u32 = 9; const LENGTH: u32 = 22; - const MASK: u32 = (1< u32 { (self.0 & Self::MASK) << Self::POSITION } @@ -971,9 +971,9 @@ impl CfgItfmConfig2 { } const fn into_raw(self) -> u32 { - ((self.scan_aux_bands as u32)<< 14) - | self.general.into_raw() - | self.antenna.into_raw() as u32 + ((self.scan_aux_bands as u32) << 14) + | self.general.into_raw() + | self.antenna.into_raw() as u32 } } @@ -997,7 +997,7 @@ pub struct CfgItfmGeneralBits(u32); impl CfgItfmGeneralBits { const POSITION: u32 = 0; const LENGTH: u32 = 12; - const MASK: u32 = (1< u32 { (self.0 & Self::MASK) << Self::POSITION } @@ -1025,12 +1025,12 @@ impl From for CfgItfmGeneralBits { pub enum CfgItfmAntennaSettings { /// Type of Antenna is not known Unknown = 0, - /// Active antenna + /// Active antenna Active = 1, /// Passive antenna Passive = 2, } - + impl From for CfgItfmAntennaSettings { fn from(cfg: u32) -> Self { let cfg = (cfg & 0x3000) >> 12; @@ -1325,8 +1325,8 @@ bitflags! { /// TP5: "Time Pulse" Config frame (32.10.38.4) #[ubx_packet_recv_send] #[ubx( - class = 0x06, - id = 0x31, + class = 0x06, + id = 0x31, fixed_payload_len = 32, flags = "default_for_builder" )] @@ -1344,17 +1344,17 @@ struct CfgTp5 { /// Frequency in Hz or Period in us, /// depending on `flags::IS_FREQ` bit #[ubx(map_type = f64, scale = 1.0)] - freq_period: u32, + freq_period: u32, /// Frequency in Hz or Period in us, /// when locked to GPS time. /// Only used when `flags::LOCKED_OTHER_SET` is set #[ubx(map_type = f64, scale = 1.0)] - freq_period_lock: u32, + freq_period_lock: u32, /// Pulse length or duty cycle, [us] or [*2^-32], /// depending on `flags::LS_LENGTH` bit #[ubx(map_type = f64, scale = 1.0)] pulse_len_ratio: u32, - /// Pulse Length in us or duty cycle (*2^-32), + /// Pulse Length in us or duty cycle (*2^-32), /// when locked to GPS time. /// Only used when `flags::LOCKED_OTHER_SET` is set #[ubx(map_type = f64, scale = 1.0)] @@ -1387,9 +1387,9 @@ impl Default for CfgTp5TimePulseMode { /// only available on `timing` receivers #[ubx_packet_recv_send] #[ubx( - class = 0x06, - id = 0x3d, - fixed_payload_len = 28, + class = 0x06, + id = 0x3d, + fixed_payload_len = 28, flags = "default_for_builder" )] struct CfgTmode2 { @@ -1397,18 +1397,18 @@ struct CfgTmode2 { #[ubx(map_type = CfgTmode2TimeXferModes, may_fail)] time_transfer_mode: u8, reserved1: u8, - #[ubx(map_type = CfgTmode2Flags)] + #[ubx(map_type = CfgTmode2Flags)] flags: u16, /// WGS84 ECEF.x coordinate in [m] or latitude in [deg° *1E-5], - /// depending on `flags` field + /// depending on `flags` field #[ubx(map_type = f64, scale = 1e-2)] ecef_x_or_lat: i32, /// WGS84 ECEF.y coordinate in [m] or longitude in [deg° *1E-5], - /// depending on `flags` field + /// depending on `flags` field #[ubx(map_type = f64, scale = 1e-2)] ecef_y_or_lon: i32, /// WGS84 ECEF.z coordinate or altitude, both in [m], - /// depending on `flags` field + /// depending on `flags` field #[ubx(map_type = f64, scale = 1e-2)] ecef_z_or_alt: i32, /// Fixed position 3D accuracy in [m] @@ -1456,7 +1456,7 @@ bitflags! { /// Time mode survey-in status #[ubx_packet_recv] #[ubx(class = 0x0d, id = 0x04, fixed_payload_len = 28)] -struct TimSvin{ +struct TimSvin { /// Passed survey-in minimum duration /// Units: s dur: u32, @@ -1474,7 +1474,7 @@ struct TimSvin{ valid: u8, /// Survey-in in progress flag, 1 = in-progress, otherwise 0 active: u8, - reserved: [u8; 2] + reserved: [u8; 2], } /// Leap second event information @@ -1545,29 +1545,29 @@ bitflags! { /// only available on `timing` receivers #[ubx_packet_recv_send] #[ubx( - class = 0x06, - id = 0x71, + class = 0x06, + id = 0x71, fixed_payload_len = 40, flags = "default_for_builder" -)] +)] struct CfgTmode3 { version: u8, reserved1: u8, /// Receiver mode, see [CfgTmode3RcvrMode] enum #[ubx(map_type = CfgTmode3RcvrMode)] rcvr_mode: u8, - #[ubx(map_type = CfgTmode3Flags)] + #[ubx(map_type = CfgTmode3Flags)] flags: u8, /// WGS84 ECEF.x coordinate in [m] or latitude in [deg° *1E-5], - /// depending on `flags` field + /// depending on `flags` field #[ubx(map_type = f64, scale = 1e-2)] ecef_x_or_lat: i32, /// WGS84 ECEF.y coordinate in [m] or longitude in [deg° *1E-5], - /// depending on `flags` field + /// depending on `flags` field #[ubx(map_type = f64, scale = 1e-2)] ecef_y_or_lon: i32, - /// WGS84 ECEF.z coordinate or altitude, both in [m], - /// depending on `flags` field + /// WGS84 ECEF.z coordinate or altitude, both in [m], + /// depending on `flags` field #[ubx(map_type = f64, scale = 1e-2)] ecef_z_or_alt: i32, /// High precision WGS84 ECEF.x coordinate in [tenths of mm], @@ -1621,7 +1621,7 @@ bitflags! { /// Uses local lock otherwise. const LOCK_GNSS_FREQ = 0x02; /// use `freq_period_lock` and `pulse_len_ratio_lock` - /// fields as soon as GPS time is valid. Uses + /// fields as soon as GPS time is valid. Uses /// `freq_period` and `pulse_len_ratio` when GPS time is invalid. const LOCKED_OTHER_SET = 0x04; /// `freq_period` and `pulse_len_ratio` fields @@ -1633,7 +1633,7 @@ bitflags! { /// Period time must be integer fraction of `1sec` /// `LOCK_GNSS_FREQ` is expected, to unlock this feature const ALIGN_TO_TOW = 0x20; - /// Pulse polarity, + /// Pulse polarity, /// 0: falling edge @ top of second, /// 1: rising edge @ top of second, const POLARITY = 0x40; @@ -2626,7 +2626,7 @@ struct MgaGloEph { e: u8, delta_tau: u8, tau: i32, - reserved2: [u8;4], + reserved2: [u8; 4], } #[ubx_packet_recv] @@ -2636,7 +2636,7 @@ struct MgaGpsIono { msg_type: u8, /// Message version: 0x00 for this version version: u8, - reserved1: [u8;2], + reserved1: [u8; 2], /// Ionospheric parameter alpha0 [s] #[ubx(map_type = f64, scale = 1.0)] // 2^-30 alpha0: i8, @@ -2661,7 +2661,7 @@ struct MgaGpsIono { /// Ionospheric parameter beta0 [s/semi-circle^3] #[ubx(map_type = f64, scale = 1.0)] // 2^-16 beta3: i8, - reserved2: [u8;4], + reserved2: [u8; 4], } #[ubx_packet_recv] @@ -2697,7 +2697,7 @@ struct MgaGpsEph { omega: i32, omega_dot: i32, idot: i16, - reserved3: [u8;2], + reserved3: [u8; 2], } /// Time pulse time data @@ -3077,7 +3077,7 @@ struct MonGnss { #[ubx_extend_bitflags] #[ubx(from, into_raw, rest_reserved)] bitflags! { - /// Selected / available Constellation Mask + /// Selected / available Constellation Mask #[derive(Default)] pub struct MonGnssConstellMask: u8 { /// GPS constellation @@ -3198,8 +3198,8 @@ struct RxmRtcm { } define_recv_packets!( - enum PacketRef { - _ = UbxUnknownPacketRef, + enum Packet { + _ = UbxUnknownPacket, NavPosLlh, NavStatus, NavDop, diff --git a/ublox_derive/src/output.rs b/ublox_derive/src/output.rs index 02cc1f5..860acca 100644 --- a/ublox_derive/src/output.rs +++ b/ublox_derive/src/output.rs @@ -8,7 +8,12 @@ use quote::{format_ident, quote}; use std::{collections::HashSet, convert::TryFrom}; use syn::{parse_quote, Ident, Type}; -fn generate_debug_impl(pack_name: &str, ref_name: &Ident, pack_descr: &PackDesc) -> TokenStream { +fn generate_debug_impl( + pack_name: &str, + ref_name: &Ident, + owned_name: &Ident, + pack_descr: &PackDesc, +) -> TokenStream { let mut fields = vec![]; for field in pack_descr.fields.iter() { let field_name = &field.name; @@ -26,12 +31,25 @@ fn generate_debug_impl(pack_name: &str, ref_name: &Ident, pack_descr: &PackDesc) .finish() } } + + impl core::fmt::Debug for #owned_name { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + f.debug_struct(#pack_name) + #(#fields)* + .finish() + } + } } } pub fn generate_recv_code_for_packet(pack_descr: &PackDesc) -> TokenStream { let pack_name = &pack_descr.name; let ref_name = format_ident!("{}Ref", pack_descr.name); + let owned_name = format_ident!("{}Owned", pack_descr.name); + let packet_size = match pack_descr.header.payload_len { + PayloadLen::Fixed(value) => value, + PayloadLen::Max(value) => value, + } as usize; let mut getters = Vec::with_capacity(pack_descr.fields.len()); let mut field_validators = Vec::new(); @@ -168,7 +186,7 @@ pub fn generate_recv_code_for_packet(pack_descr: &PackDesc) -> TokenStream { } }; - let debug_impl = generate_debug_impl(pack_name, &ref_name, pack_descr); + let debug_impl = generate_debug_impl(pack_name, &ref_name, &owned_name, pack_descr); quote! { #[doc = #struct_comment] @@ -180,11 +198,46 @@ pub fn generate_recv_code_for_packet(pack_descr: &PackDesc) -> TokenStream { self.0 } + pub fn to_owned(&self) -> #owned_name { + self.into() + } + + #(#getters)* + + #validator + } + + #[doc = #struct_comment] + #[doc = "Owns the underlying buffer of data, contains accessor methods to retrieve data."] + pub struct #owned_name([u8; #packet_size]); + + impl #owned_name { + const PACKET_SIZE: usize = #packet_size; + + #[inline] + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } + #(#getters)* #validator } + impl<'a> From<&#ref_name<'a>> for #owned_name { + fn from(packet: &#ref_name<'a>) -> Self { + let mut bytes = [0u8; #packet_size]; + bytes.clone_from_slice(packet.as_bytes()); + Self(bytes) + } + } + + impl<'a> From<#ref_name<'a>> for #owned_name { + fn from(packet: #ref_name<'a>) -> Self { + (&packet).into() + } + } + #debug_impl } } @@ -247,7 +300,11 @@ pub fn generate_send_code_for_packet(pack_descr: &PackDesc) -> TokenStream { builder_needs_lifetime = true; - assert_eq!(fi, pack_descr.fields.len() - 1, "Iterator field must be the last field."); + assert_eq!( + fi, + pack_descr.fields.len() - 1, + "Iterator field must be the last field." + ); break; } }; @@ -600,30 +657,52 @@ pub fn generate_code_to_extend_bitflags(bitflags: BitFlagsMacro) -> syn::Result< } pub fn generate_code_for_parse(recv_packs: &RecvPackets) -> TokenStream { - let union_enum_name = &recv_packs.union_enum_name; + let union_enum_name_ref = format_ident!("{}Ref", &recv_packs.union_enum_name); + let union_enum_name_owned = format_ident!("{}Owned", &recv_packs.union_enum_name); - let mut pack_enum_variants = Vec::with_capacity(recv_packs.all_packets.len()); - let mut matches = Vec::with_capacity(recv_packs.all_packets.len()); - let mut class_id_matches = Vec::with_capacity(recv_packs.all_packets.len()); + let mut pack_enum_variants_ref = Vec::with_capacity(recv_packs.all_packets.len()); + let mut pack_enum_variants_owned = Vec::with_capacity(recv_packs.all_packets.len()); + let mut matches_ref = Vec::with_capacity(recv_packs.all_packets.len()); + let mut matches_owned = Vec::with_capacity(recv_packs.all_packets.len()); + let mut matches_ref_to_owned = Vec::with_capacity(recv_packs.all_packets.len()); + let mut class_id_matches_ref = Vec::with_capacity(recv_packs.all_packets.len()); + let mut class_id_matches_owned = Vec::with_capacity(recv_packs.all_packets.len()); for name in &recv_packs.all_packets { let ref_name = format_ident!("{}Ref", name); - pack_enum_variants.push(quote! { + let owned_name = format_ident!("{}Owned", name); + + pack_enum_variants_ref.push(quote! { #name(#ref_name <'a>) }); + pack_enum_variants_owned.push(quote! { + #name(#owned_name) + }); - matches.push(quote! { + matches_ref.push(quote! { (#name::CLASS, #name::ID) if <#ref_name>::validate(payload).is_ok() => { - Ok(#union_enum_name::#name(#ref_name(payload))) + Ok(#union_enum_name_ref::#name(#ref_name(payload))) + } + }); + matches_owned.push(quote! { + (#name::CLASS, #name::ID) if <#owned_name>::validate(payload).is_ok() => { + Ok(#union_enum_name_owned::#name(#owned_name([0; #owned_name::PACKET_SIZE]))) } }); + matches_ref_to_owned.push(quote! { + #union_enum_name_ref::#name(packet) => #union_enum_name_owned::#name(packet.into()), + }); - class_id_matches.push(quote! { - #union_enum_name::#name(_) => (#name::CLASS, #name::ID) + class_id_matches_ref.push(quote! { + #union_enum_name_ref::#name(_) => (#name::CLASS, #name::ID) + }); + class_id_matches_owned.push(quote! { + #union_enum_name_owned::#name(_) => (#name::CLASS, #name::ID) }); } - let unknown_var = &recv_packs.unknown_ty; + let unknown_var_ref = format_ident!("{}Ref", &recv_packs.unknown_ty); + let unknown_var_owned = format_ident!("{}Owned", &recv_packs.unknown_ty); let max_payload_len_calc = recv_packs .all_packets @@ -635,30 +714,69 @@ pub fn generate_code_for_parse(recv_packs: &RecvPackets) -> TokenStream { quote! { #[doc = "All possible packets enum"] #[derive(Debug)] - pub enum #union_enum_name<'a> { - #(#pack_enum_variants),*, - Unknown(#unknown_var<'a>) + pub enum #union_enum_name_ref<'a> { + #(#pack_enum_variants_ref),*, + Unknown(#unknown_var_ref<'a>) + } + #[doc = "All possible packets enum, owning the underlying data"] + #[derive(Debug)] + pub enum #union_enum_name_owned { + #(#pack_enum_variants_owned),*, + Unknown(#unknown_var_owned) } - impl<'a> #union_enum_name<'a> { + impl<'a> #union_enum_name_ref<'a> { pub fn class_and_msg_id(&self) -> (u8, u8) { match *self { - #(#class_id_matches),*, - #union_enum_name::Unknown(ref pack) => (pack.class, pack.msg_id), + #(#class_id_matches_ref),*, + #union_enum_name_ref::Unknown(ref pack) => (pack.class, pack.msg_id), + } + } + + pub fn to_owned(&self) -> #union_enum_name_owned { + self.into() + } + } + impl #union_enum_name_owned { + pub fn class_and_msg_id(&self) -> (u8, u8) { + match *self { + #(#class_id_matches_owned),*, + #union_enum_name_owned::Unknown(ref pack) => (pack.class, pack.msg_id), } } } - pub(crate) fn match_packet(class: u8, msg_id: u8, payload: &[u8]) -> Result<#union_enum_name, ParserError> { + pub(crate) fn match_packet(class: u8, msg_id: u8, payload: &[u8]) -> Result<#union_enum_name_ref, ParserError> { match (class, msg_id) { - #(#matches)* - _ => Ok(#union_enum_name::Unknown(#unknown_var { + #(#matches_ref)* + _ => Ok(#union_enum_name_ref::Unknown(#unknown_var_ref { payload, class, msg_id })), } } + pub(crate) fn match_packet_owned(class: u8, msg_id: u8, payload: &[u8]) -> Result<#union_enum_name_owned, ParserError> { + match (class, msg_id) { + #(#matches_owned)* + _ => Ok(#union_enum_name_owned::Unknown(#unknown_var_owned { + payload: payload.into(), + class, + msg_id + })), + } + } + + impl<'a> From<&#union_enum_name_ref<'a>> for #union_enum_name_owned { + fn from(packet: &#union_enum_name_ref<'a>) -> Self { + match packet { + #(#matches_ref_to_owned)* + #union_enum_name_ref::Unknown(#unknown_var_ref {payload, class, msg_id}) => { + PacketOwned::Unknown(#unknown_var_owned { payload: payload.to_vec(), class: *class, msg_id: *msg_id }) + }, + } + } + } const fn max_u16(a: u16, b: u16) -> u16 { [a, b][(a < b) as usize] From 0d57b36a8c433a57cfb714f8299b0d3427d61082 Mon Sep 17 00:00:00 2001 From: Maxime Borges Date: Mon, 26 Sep 2022 20:22:13 +0200 Subject: [PATCH 2/3] PacketOwned: Fix match_packet_owned implementation --- ublox_derive/src/output.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ublox_derive/src/output.rs b/ublox_derive/src/output.rs index 860acca..0c84414 100644 --- a/ublox_derive/src/output.rs +++ b/ublox_derive/src/output.rs @@ -686,7 +686,9 @@ pub fn generate_code_for_parse(recv_packs: &RecvPackets) -> TokenStream { }); matches_owned.push(quote! { (#name::CLASS, #name::ID) if <#owned_name>::validate(payload).is_ok() => { - Ok(#union_enum_name_owned::#name(#owned_name([0; #owned_name::PACKET_SIZE]))) + let mut bytes = [0u8; #owned_name::PACKET_SIZE]; + bytes.clone_from_slice(payload); + Ok(#union_enum_name_owned::#name(#owned_name(bytes))) } }); matches_ref_to_owned.push(quote! { From 29a693f1bf30d6b1872c7cf2c3131fe9ca177fd0 Mon Sep 17 00:00:00 2001 From: Maxime Borges Date: Mon, 26 Sep 2022 20:10:22 +0200 Subject: [PATCH 3/3] Fix tests --- ublox_derive/src/tests.rs | 308 ++++++++++++++++++++++++++++++++++---- 1 file changed, 280 insertions(+), 28 deletions(-) diff --git a/ublox_derive/src/tests.rs b/ublox_derive/src/tests.rs index aee3540..1edf433 100644 --- a/ublox_derive/src/tests.rs +++ b/ublox_derive/src/tests.rs @@ -39,14 +39,12 @@ fn test_ubx_packet_recv_simple() { quote! { #[doc = "Some comment"] pub struct Test; - impl UbxPacketMeta for Test { const CLASS: u8 = 1u8; const ID: u8 = 2u8; const FIXED_PAYLOAD_LEN: Option = Some(16u16); const MAX_PAYLOAD_LEN: u16 = 16u16; } - #[doc = "Some comment"] #[doc = "Contains a reference to an underlying buffer, contains accessor methods to retrieve data."] pub struct TestRef<'a>(&'a [u8]); @@ -55,7 +53,9 @@ fn test_ubx_packet_recv_simple() { pub fn as_bytes(&self) -> &[u8] { self.0 } - + pub fn to_owned(&self) -> TestOwned { + self.into() + } #[doc = ""] #[inline] pub fn itow(&self) -> u32 { @@ -63,8 +63,8 @@ fn test_ubx_packet_recv_simple() { self.0[0usize], self.0[1usize], self.0[2usize], - self.0[3usize]] - ); + self.0[3usize], + ]); val } #[doc = "this is lat"] @@ -74,8 +74,8 @@ fn test_ubx_packet_recv_simple() { self.0[4usize], self.0[5usize], self.0[6usize], - self.0[7usize]] - ); + self.0[7usize], + ]); val } #[doc = "this is lat"] @@ -85,8 +85,8 @@ fn test_ubx_packet_recv_simple() { self.0[4usize], self.0[5usize], self.0[6usize], - self.0[7usize]] - ); + self.0[7usize], + ]); let val = ::from(val); let val = val * 1e-7; val @@ -128,21 +128,141 @@ fn test_ubx_packet_recv_simple() { let val = ::from_le_bytes([self.0[15usize]]); val } - fn validate(payload: &[u8]) -> Result<(), ParserError> { let expect = 16usize; let got = payload.len(); - if got == expect { + if got == expect { let val = payload[14usize]; if !::is_valid(val) { - return Err(ParserError::InvalidField{packet: "Test", field: stringify!(flags)}); + return Err(ParserError::InvalidField { + packet: "Test", + field: stringify!(flags), + }); } Ok(()) } else { - Err(ParserError::InvalidPacketLen{packet: "Test", expect, got}) + Err(ParserError::InvalidPacketLen { + packet: "Test", + expect, + got, + }) } } } + #[doc = "Some comment"] + #[doc = "Owns the underlying buffer of data, contains accessor methods to retrieve data."] + pub struct TestOwned([u8; 16usize]); + impl TestOwned { + const PACKET_SIZE: usize = 16usize; + #[inline] + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } + #[doc = ""] + #[inline] + pub fn itow(&self) -> u32 { + let val = ::from_le_bytes([ + self.0[0usize], + self.0[1usize], + self.0[2usize], + self.0[3usize], + ]); + val + } + #[doc = "this is lat"] + #[inline] + pub fn lat_degrees_raw(&self) -> i32 { + let val = ::from_le_bytes([ + self.0[4usize], + self.0[5usize], + self.0[6usize], + self.0[7usize], + ]); + val + } + #[doc = "this is lat"] + #[inline] + pub fn lat_degrees(&self) -> f64 { + let val = ::from_le_bytes([ + self.0[4usize], + self.0[5usize], + self.0[6usize], + self.0[7usize], + ]); + let val = ::from(val); + let val = val * 1e-7; + val + } + #[doc = "this is a"] + #[inline] + pub fn a(&self) -> u8 { + let val = self.0[8usize]; + val + } + #[doc = ""] + #[inline] + pub fn reserved1(&self) -> [u8; 5] { + let val = [ + self.0[9usize], + self.0[10usize], + self.0[11usize], + self.0[12usize], + self.0[13usize], + ]; + val + } + #[doc = ""] + #[inline] + pub fn flags_raw(&self) -> u8 { + let val = self.0[14usize]; + val + } + #[doc = ""] + #[inline] + pub fn flags(&self) -> Flags { + let val = self.0[14usize]; + let val = ::from_unchecked(val); + val + } + #[doc = ""] + #[inline] + pub fn b(&self) -> i8 { + let val = ::from_le_bytes([self.0[15usize]]); + val + } + fn validate(payload: &[u8]) -> Result<(), ParserError> { + let expect = 16usize; + let got = payload.len(); + if got == expect { + let val = payload[14usize]; + if !::is_valid(val) { + return Err(ParserError::InvalidField { + packet: "Test", + field: stringify!(flags), + }); + } + Ok(()) + } else { + Err(ParserError::InvalidPacketLen { + packet: "Test", + expect, + got, + }) + } + } + } + impl<'a> From<&TestRef<'a>> for TestOwned { + fn from(packet: &TestRef<'a>) -> Self { + let mut bytes = [0u8; 16usize]; + bytes.clone_from_slice(packet.as_bytes()); + Self(bytes) + } + } + impl<'a> From> for TestOwned { + fn from(packet: TestRef<'a>) -> Self { + (&packet).into() + } + } impl core::fmt::Debug for TestRef<'_> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("Test") @@ -155,6 +275,18 @@ fn test_ubx_packet_recv_simple() { .finish() } } + impl core::fmt::Debug for TestOwned { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + f.debug_struct("Test") + .field(stringify!(itow), &self.itow()) + .field(stringify!(lat), &self.lat_degrees()) + .field(stringify!(a), &self.a()) + .field(stringify!(reserved1), &self.reserved1()) + .field(stringify!(flags), &self.flags()) + .field(stringify!(b), &self.b()) + .finish() + } + } }, ); } @@ -185,14 +317,12 @@ fn test_ubx_packet_recv_dyn_len() { quote! { #[doc = ""] pub struct Test; - impl UbxPacketMeta for Test { const CLASS: u8 = 1u8; const ID: u8 = 2u8; const FIXED_PAYLOAD_LEN: Option = None; const MAX_PAYLOAD_LEN: u16 = 38u16; } - #[doc = ""] #[doc = "Contains a reference to an underlying buffer, contains accessor methods to retrieve data."] pub struct TestRef<'a>(&'a [u8]); @@ -201,14 +331,56 @@ fn test_ubx_packet_recv_dyn_len() { pub fn as_bytes(&self) -> &[u8] { self.0 } - + pub fn to_owned(&self) -> TestOwned { + self.into() + } + #[doc = ""] + #[inline] + pub fn f1_raw(&self) -> &[u8] { + let val = &self.0[0usize..(0usize + 8usize)]; + val + } + #[doc = ""] + #[inline] + pub fn f1(&self) -> &str { + let val = &self.0[0usize..(0usize + 8usize)]; + let val = unpack_str(val); + val + } + #[doc = ""] + #[inline] + pub fn rest(&self) -> &[u8] { + &self.0[8usize..] + } + fn validate(payload: &[u8]) -> Result<(), ParserError> { + let min = 8usize; + let got = payload.len(); + if got >= min { + Ok(()) + } else { + Err(ParserError::InvalidPacketLen { + packet: "Test", + expect: min, + got, + }) + } + } + } + #[doc = ""] + #[doc = "Owns the underlying buffer of data, contains accessor methods to retrieve data."] + pub struct TestOwned([u8; 38usize]); + impl TestOwned { + const PACKET_SIZE: usize = 38usize; + #[inline] + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } #[doc = ""] #[inline] pub fn f1_raw(&self) -> &[u8] { let val = &self.0[0usize..(0usize + 8usize)]; val } - #[doc = ""] #[inline] pub fn f1(&self) -> &str { @@ -216,23 +388,37 @@ fn test_ubx_packet_recv_dyn_len() { let val = unpack_str(val); val } - #[doc = ""] #[inline] pub fn rest(&self) -> &[u8] { &self.0[8usize..] } - fn validate(payload: &[u8]) -> Result<(), ParserError> { let min = 8usize; let got = payload.len(); if got >= min { Ok(()) } else { - Err(ParserError::InvalidPacketLen{packet: "Test", expect: min, got}) + Err(ParserError::InvalidPacketLen { + packet: "Test", + expect: min, + got, + }) } } } + impl<'a> From<&TestRef<'a>> for TestOwned { + fn from(packet: &TestRef<'a>) -> Self { + let mut bytes = [0u8; 38usize]; + bytes.clone_from_slice(packet.as_bytes()); + Self(bytes) + } + } + impl<'a> From> for TestOwned { + fn from(packet: TestRef<'a>) -> Self { + (&packet).into() + } + } impl core::fmt::Debug for TestRef<'_> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("Test") @@ -241,6 +427,14 @@ fn test_ubx_packet_recv_dyn_len() { .finish() } } + impl core::fmt::Debug for TestOwned { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + f.debug_struct("Test") + .field(stringify!(f1), &self.f1()) + .field(stringify!(rest), &self.rest()) + .finish() + } + } }, ); } @@ -433,8 +627,8 @@ fn test_upgrade_enum() { #[test] fn test_define_recv_packets() { let src_code = quote! { - enum PacketRef { - _ = UnknownPacketRef, + enum Packet { + _ = UnknownPacket, Pack1, Pack2 } @@ -452,9 +646,15 @@ fn test_define_recv_packets() { pub enum PacketRef<'a> { Pack1(Pack1Ref<'a>), Pack2(Pack2Ref<'a>), - Unknown(UnknownPacketRef<'a>) + Unknown(UnknownPacketRef<'a>), + } + #[doc = "All possible packets enum, owning the underlying data"] + #[derive(Debug)] + pub enum PacketOwned { + Pack1(Pack1Owned), + Pack2(Pack2Owned), + Unknown(UnknownPacketOwned), } - impl<'a> PacketRef<'a> { pub fn class_and_msg_id(&self) -> (u8, u8) { match *self { @@ -463,8 +663,19 @@ fn test_define_recv_packets() { PacketRef::Unknown(ref pack) => (pack.class, pack.msg_id), } } + pub fn to_owned(&self) -> PacketOwned { + self.into() + } + } + impl PacketOwned { + pub fn class_and_msg_id(&self) -> (u8, u8) { + match *self { + PacketOwned::Pack1(_) => (Pack1::CLASS, Pack1::ID), + PacketOwned::Pack2(_) => (Pack2::CLASS, Pack2::ID), + PacketOwned::Unknown(ref pack) => (pack.class, pack.msg_id), + } + } } - pub(crate) fn match_packet( class: u8, msg_id: u8, @@ -484,12 +695,53 @@ fn test_define_recv_packets() { })), } } - + pub(crate) fn match_packet_owned( + class: u8, + msg_id: u8, + payload: &[u8], + ) -> Result { + match (class, msg_id) { + (Pack1::CLASS, Pack1::ID) if ::validate(payload).is_ok() => { + let mut bytes = [0u8; Pack1Owned::PACKET_SIZE]; + bytes.clone_from_slice(payload); + Ok(PacketOwned::Pack1(Pack1Owned(bytes))) + } + (Pack2::CLASS, Pack2::ID) if ::validate(payload).is_ok() => { + let mut bytes = [0u8; Pack2Owned::PACKET_SIZE]; + bytes.clone_from_slice(payload); + Ok(PacketOwned::Pack2(Pack2Owned(bytes))) + } + _ => Ok(PacketOwned::Unknown(UnknownPacketOwned { + payload: payload.into(), + class, + msg_id, + })), + } + } + impl<'a> From<&PacketRef<'a>> for PacketOwned { + fn from(packet: &PacketRef<'a>) -> Self { + match packet { + PacketRef::Pack1(packet) => PacketOwned::Pack1(packet.into()), + PacketRef::Pack2(packet) => PacketOwned::Pack2(packet.into()), + PacketRef::Unknown(UnknownPacketRef { + payload, + class, + msg_id, + }) => PacketOwned::Unknown(UnknownPacketOwned { + payload: payload.to_vec(), + class: *class, + msg_id: *msg_id, + }), + } + } + } const fn max_u16(a: u16, b: u16) -> u16 { [a, b][(a < b) as usize] } - pub(crate) const MAX_PAYLOAD_LEN: u16 = - max_u16(Pack2::MAX_PAYLOAD_LEN, max_u16(Pack1::MAX_PAYLOAD_LEN, 0u16)); + pub(crate) const MAX_PAYLOAD_LEN: u16 = max_u16( + Pack2::MAX_PAYLOAD_LEN, + max_u16(Pack1::MAX_PAYLOAD_LEN, 0u16), + ); }, ); }