Expand description
§MQTT Protocol Core
A Sans-I/O style MQTT protocol library for Rust that supports both MQTT v5.0 and v3.1.1.
This library provides a pure protocol implementation without any I/O operations, making it suitable for use with any async runtime or synchronous I/O framework. All operations are synchronous and the library focuses solely on MQTT protocol message parsing, validation, and generation.
§Features
- Sans-I/O Design: Pure protocol implementation with no I/O dependencies
- Dual Version Support: Full support for both MQTT v3.1.1 and v5.0
- Generic Packet ID: Supports custom packet ID types (u16, u32) for broker clustering
- Zero-Copy Payload: Efficient payload handling with
ArcPayload
- Comprehensive: All MQTT packet types and features supported
- Type Safety: Compile-time role and version checking
§Quick Start
§Basic Client Connection
use mqtt_protocol_core::mqtt::{
Connection, Version,
connection::role::Client,
packet::v5_0::Connect,
};
// Create a client connection for MQTT v5.0
let mut client = Connection::<Client>::new(Version::V5_0);
// Create a CONNECT packet
let connect = Connect::builder()
.client_id("my-client")
.unwrap()
.clean_start(true)
.build()
.unwrap();
// Send the packet through the connection
let events = client.send(connect.into());
§Server with Version Auto-Detection
use mqtt_protocol_core::mqtt::{
Connection, Version,
connection::role::Server,
};
// Create a server that accepts any MQTT version
let mut server = Connection::<Server>::new(Version::Undetermined);
// The server will automatically adapt to the client's protocol version
// when it receives a CONNECT packet
§Architecture
The library is organized into several key modules:
mqtt::connection
- Connection state management and packet processingmqtt::packet
- MQTT packet definitions for v3.1.1 and v5.0mqtt::Version
- Protocol version handlingmqtt::ArcPayload
- Efficient payload management
§Sans-I/O Pattern
This library follows the Sans-I/O pattern, meaning it handles protocol logic without performing any I/O operations. Instead, it returns events that tell your application what actions to take:
use mqtt_protocol_core::mqtt::{
Connection, Version,
connection::{role::Client, event::GenericEvent},
common::Cursor,
};
let mut client = Connection::<Client>::new(Version::V5_0);
let data = &[0u8; 0][..];
let mut data_cursor = Cursor::new(data);
let events = client.recv(&mut data_cursor);
for event in events {
match event {
GenericEvent::RequestSendPacket { packet, .. } => {
// Send packet over network
}
GenericEvent::NotifyPacketReceived(packet) => {
// Handle received packet
}
// ... other events
_ => {}
}
}
§Generic Packet ID Support
The library supports custom packet ID types for advanced use cases like broker clustering, where u32 packet IDs can prevent ID exhaustion:
use mqtt_protocol_core::mqtt::{GenericConnection, connection::role::Server};
// Use u32 packet IDs instead of standard u16
let mut server = GenericConnection::<Server, u32>::new(
mqtt_protocol_core::mqtt::Version::V5_0
);
§No-std Support
This library fully supports no_std
environments for embedded systems.
To use in a no_std
environment, disable the default std
feature:
[dependencies]
mqtt-protocol-core = { version = "0.7.1", default-features = false }
No-std usage example:
#![no_std]
extern crate alloc;
use alloc::{vec::Vec, string::String};
use mqtt_protocol_core::mqtt::{
Connection, Version,
connection::role::Client,
packet::v5_0::Connect,
common::Cursor,
};
fn main() {
// Create client connection
let mut client = Connection::<Client>::new(Version::V5_0);
// Create CONNECT packet
let connect = Connect::builder()
.client_id("embedded-client")
.unwrap()
.clean_start(true)
.build()
.unwrap();
// Send packet and handle events
let events = client.send(connect.into());
// Process events in your embedded application
for event in events {
match event {
// Handle RequestSendPacket, NotifyPacketReceived, etc.
_ => {}
}
}
}
§Feature Flags
§Core Features
std
(default): Enables standard library support, includingstd::io::IoSlice
for vectored I/Otracing
: Enables logging support via thetracing
crate. When disabled, trace statements compile to no-ops with zero overhead
# Enable tracing support (independent of std)
[dependencies]
mqtt-protocol-core = { version = "0.7.1", default-features = false, features = ["tracing"] }
# Use with std but without tracing overhead
[dependencies]
mqtt-protocol-core = { version = "0.7.1", default-features = false, features = ["std"] }
# Full-featured (std + tracing)
[dependencies]
mqtt-protocol-core = { version = "0.7.1", features = ["tracing"] }
§Small String Optimization (SSO) Features
These features optimize memory usage for small strings and binary data by storing them on the stack instead of allocating on the heap:
-
sso-min-32bit
: Minimal optimization for 32-bit environments- MqttString/MqttBinary: 12 bytes total buffer
- ArcPayload: 15 bytes data buffer
-
sso-min-64bit
: Recommended optimization for 64-bit environments- MqttString/MqttBinary: 24 bytes total buffer
- ArcPayload: 31 bytes data buffer
-
sso-lv10
: Level 10 optimization for moderate performance gains- MqttString/MqttBinary: 24 bytes total buffer
- ArcPayload: 127 bytes data buffer
-
sso-lv20
: Level 20 optimization for maximum performance- MqttString/MqttBinary: 48 bytes total buffer
- ArcPayload: 255 bytes data buffer
# Use specific SSO optimization level
[dependencies]
mqtt-protocol-core = { version = "0.7.1", features = ["sso-lv10"] }
# Combine with other features
[dependencies]
mqtt-protocol-core = { version = "0.7.1", features = ["std", "sso-lv20", "tracing"] }
§⚠️ Critical: SSO Feature Flag Propagation
When your crate depends on mqtt-protocol-core and is consumed by other crates, you should re-export all SSO feature flags to ensure proper feature selection.
Multiple SSO Features: When multiple SSO features are enabled simultaneously,
the system automatically selects the largest buffer size from the enabled features.
This allows safe usage with --all-features
and prevents compilation errors.
Feature Selection Priority:
sso-lv20
(highest): 48-byte String/Binary, 255-byte ArcPayloadsso-lv10
orsso-min-64bit
: 24-byte String/Binary, 127-byte ArcPayloadsso-min-32bit
(lowest): 12-byte String/Binary, 15-byte ArcPayload
Required pattern for library authors:
# Your crate's Cargo.toml
[dependencies]
mqtt-protocol-core = { version = "0.7.1", features = ["sso-lv10"] }
[features]
# MANDATORY: Re-export ALL SSO features to allow downstream configuration
sso-min-32bit = ["mqtt-protocol-core/sso-min-32bit"]
sso-min-64bit = ["mqtt-protocol-core/sso-min-64bit"]
sso-lv10 = ["mqtt-protocol-core/sso-lv10"]
sso-lv20 = ["mqtt-protocol-core/sso-lv20"]
This pattern ensures that when multiple dependency crates enable different SSO levels, the final application receives the maximum optimization level from all dependencies.
Key points for no-std usage:
- Use
extern crate alloc;
to enable heap allocations - Import types from
alloc
crate instead ofstd
IoSlice
functionality is not available inno_std
mode- Tracing can be enabled independently of
std
for embedded debugging