diff --git a/mqtt-format/src/lib.rs b/mqtt-format/src/lib.rs index bed8140f..960ab547 100644 --- a/mqtt-format/src/lib.rs +++ b/mqtt-format/src/lib.rs @@ -9,5 +9,6 @@ #[cfg(feature = "mqttv3")] pub mod v3; + #[cfg(feature = "mqttv5")] pub mod v5; diff --git a/mqtt-format/src/v5/bytes.rs b/mqtt-format/src/v5/bytes.rs index f9a2a016..b1623c29 100644 --- a/mqtt-format/src/v5/bytes.rs +++ b/mqtt-format/src/v5/bytes.rs @@ -3,6 +3,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // +//! Ways to parse MQTT byte data use winnow::binary::length_take; use winnow::Bytes; diff --git a/mqtt-format/src/v5/fixed_header.rs b/mqtt-format/src/v5/fixed_header.rs index eb902045..ed0ba85a 100644 --- a/mqtt-format/src/v5/fixed_header.rs +++ b/mqtt-format/src/v5/fixed_header.rs @@ -3,6 +3,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // +//! Everything around parsing the fixed MQTT Header use winnow::binary::bits::bits; use winnow::error::ErrMode; diff --git a/mqtt-format/src/v5/integers.rs b/mqtt-format/src/v5/integers.rs index 96fa8aaa..91990cc6 100644 --- a/mqtt-format/src/v5/integers.rs +++ b/mqtt-format/src/v5/integers.rs @@ -3,6 +3,9 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // +//! Various ways to parse MQTT integers +//! +//! All integers in MQTT are big-endian use winnow::combinator::trace; use winnow::token::take_while; @@ -11,6 +14,10 @@ use winnow::Parser; use super::MResult; +/// Parse a u16 +/// +/// MQTT expects their numbers in big-endian +#[doc = crate::v5::util::md_speclink!("_Toc3901008")] pub fn parse_u16(input: &mut &Bytes) -> MResult { trace( "mqtt_u16", @@ -19,6 +26,10 @@ pub fn parse_u16(input: &mut &Bytes) -> MResult { .parse_next(input) } +/// Parse a u32 +/// +/// MQTT expects their numbers in big-endian +#[doc = crate::v5::util::md_speclink!("_Toc3901009")] pub fn parse_u32(input: &mut &Bytes) -> MResult { trace( "mqtt_u32", @@ -27,6 +38,12 @@ pub fn parse_u32(input: &mut &Bytes) -> MResult { .parse_next(input) } +/// Parse a variable sized integer +/// +/// Value range: `0..268_435_455` +/// The maximal value is smaller than a u32, so that type is used +/// +#[doc = crate::v5::util::md_speclink!("_Toc3901011")] pub fn parse_variable_u32(input: &mut &Bytes) -> MResult { trace("mqtt_variable_u32", |input: &mut &Bytes| { let var_bytes = ( diff --git a/mqtt-format/src/v5/level.rs b/mqtt-format/src/v5/level.rs deleted file mode 100644 index 7e54eefb..00000000 --- a/mqtt-format/src/v5/level.rs +++ /dev/null @@ -1,30 +0,0 @@ -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. -// - -use winnow::error::ErrMode; -use winnow::error::ParserError; -use winnow::Bytes; - -use super::MResult; - -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum ProtocolLevel { - V3, - V5, -} - -impl ProtocolLevel { - pub fn parse(input: &mut &Bytes) -> MResult { - match winnow::binary::u8(input)? { - 3 => Ok(Self::V3), - 5 => Ok(Self::V5), - _ => Err(ErrMode::from_error_kind( - input, - winnow::error::ErrorKind::Verify, - )), - } - } -} diff --git a/mqtt-format/src/v5/mod.rs b/mqtt-format/src/v5/mod.rs index b11a53a0..11527903 100644 --- a/mqtt-format/src/v5/mod.rs +++ b/mqtt-format/src/v5/mod.rs @@ -8,15 +8,41 @@ #![deny(clippy::std_instead_of_core)] #![deny(clippy::alloc_instead_of_core)] +//! MQTTv5 binary format parsing +//! +#![doc = util::md_speclink!("_Toc3901000")] +//! +//! The main entry point of the v5 module is found in [packets::MqttPacket] and its associated +//! [packets::MqttPacket::parse_complete] method. +//! +//! This allows to retrieve a zero-copy deserialized form of a single MQTTPacket. +//! All protocol-level invariants are checked here. Nonetheless, dynamic protocol violations cannot +//! be detected at this level, and as such fall onto the responsibility of the user. +//! +//! # Example +//! +//! ```rust +//! use mqtt_format::v5::packets::MqttPacket; +//!# // A PINGREQ packet +//!# fn read_input() -> &'static [u8] { &[0b1100_0000, 0x0] } +//! let input: &[u8] = read_input(); +//! +//! let packet = MqttPacket::parse_complete(input).expect("A valid MQTT Packet"); +//! +//! match packet { +//! MqttPacket::Pingreq(_) => println!("Got a PINGREQ!"), +//! packet => panic!("Got an unexpected packet: {packet:?}"), +//! } +//! ``` + pub mod bytes; pub mod fixed_header; pub mod integers; -pub mod level; pub mod packets; pub mod properties; pub mod reason_code; pub mod strings; -pub mod util; +mod util; pub mod variable_header; pub type MResult = winnow::PResult; diff --git a/mqtt-format/src/v5/packets/connect.rs b/mqtt-format/src/v5/packets/connect.rs index fa90f777..61a99017 100644 --- a/mqtt-format/src/v5/packets/connect.rs +++ b/mqtt-format/src/v5/packets/connect.rs @@ -13,7 +13,6 @@ use winnow::Parser; use crate::v5::bytes::parse_binary_data; use crate::v5::fixed_header::QualityOfService; use crate::v5::integers::parse_u16; -use crate::v5::level::ProtocolLevel; use crate::v5::strings::parse_string; use crate::v5::variable_header::AuthenticationData; use crate::v5::variable_header::AuthenticationMethod; @@ -183,3 +182,22 @@ crate::v5::properties::define_properties! { correlation_data: CorrelationData<'i>, } } + +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum ProtocolLevel { + V3, + V5, +} + +impl ProtocolLevel { + pub fn parse(input: &mut &Bytes) -> MResult { + match winnow::binary::u8(input)? { + 3 => Ok(Self::V3), + 5 => Ok(Self::V5), + _ => Err(ErrMode::from_error_kind( + input, + winnow::error::ErrorKind::Verify, + )), + } + } +} diff --git a/mqtt-format/src/v5/packets/mod.rs b/mqtt-format/src/v5/packets/mod.rs index 6b7737c7..5988b11f 100644 --- a/mqtt-format/src/v5/packets/mod.rs +++ b/mqtt-format/src/v5/packets/mod.rs @@ -3,6 +3,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // +//! Handling of MQTT Control Packets use winnow::error::ContextError; use winnow::error::ErrMode; diff --git a/mqtt-format/src/v5/properties.rs b/mqtt-format/src/v5/properties.rs index 3ab5796a..506deb5a 100644 --- a/mqtt-format/src/v5/properties.rs +++ b/mqtt-format/src/v5/properties.rs @@ -3,6 +3,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // +//! Handling of MQTT Properties that are present in some packets use winnow::error::ErrMode; use winnow::error::ParserError; diff --git a/mqtt-format/src/v5/reason_code.rs b/mqtt-format/src/v5/reason_code.rs index ea360005..10d88f2a 100644 --- a/mqtt-format/src/v5/reason_code.rs +++ b/mqtt-format/src/v5/reason_code.rs @@ -3,6 +3,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // +//! Handling reason codes that are present in some packets macro_rules! make_combined_reason_code { (pub enum $name:ident { diff --git a/mqtt-format/src/v5/strings.rs b/mqtt-format/src/v5/strings.rs index 48fb102d..91c81e16 100644 --- a/mqtt-format/src/v5/strings.rs +++ b/mqtt-format/src/v5/strings.rs @@ -3,6 +3,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // +//! Various ways to parse MQTT Strings use winnow::binary::length_take; use winnow::error::ErrMode; @@ -13,6 +14,11 @@ use winnow::Parser; use super::integers::parse_u16; use super::MResult; +/// Parse an UTF-8 String +/// +/// MQTT expects that all Strings are UTF-8 encoded +/// +#[doc = crate::v5::util::md_speclink!("_Toc3901010")] pub fn parse_string<'i>(input: &mut &'i Bytes) -> MResult<&'i str> { winnow::combinator::trace("mqtt_string", |input: &mut &'i Bytes| { let maybe_str = length_take(parse_u16).parse_next(input)?; @@ -23,7 +29,12 @@ pub fn parse_string<'i>(input: &mut &'i Bytes) -> MResult<&'i str> { .parse_next(input) } -pub fn string_pair<'i>(input: &mut &'i Bytes) -> MResult<(&'i str, &'i str)> { +/// Parse a pair of UTF-8 Strings +/// +/// MQTT expects that all Strings are UTF-8 encoded +/// +#[doc = crate::v5::util::md_speclink!("_Toc3901013")] +pub fn parse_string_pair<'i>(input: &mut &'i Bytes) -> MResult<(&'i str, &'i str)> { winnow::combinator::trace("mqtt_string_pair", |input: &mut &'i Bytes| { let first = parse_string(input)?; let second = parse_string(input)?; diff --git a/mqtt-format/src/v5/variable_header.rs b/mqtt-format/src/v5/variable_header.rs index fca22e06..011efe3a 100644 --- a/mqtt-format/src/v5/variable_header.rs +++ b/mqtt-format/src/v5/variable_header.rs @@ -3,6 +3,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // +//! Various components present in MQTT variable headers use winnow::Bytes; use winnow::Parser; @@ -175,7 +176,7 @@ pub struct UserProperty<'i> { impl<'i> UserProperty<'i> { pub fn parse(input: &mut &'i Bytes) -> MResult> { winnow::combinator::trace("UserProperty", |input: &mut &'i Bytes| { - crate::v5::strings::string_pair + crate::v5::strings::parse_string_pair .map(|(k, v)| UserProperty { key: k, value: v }) .parse_next(input) })