#![cfg_attr(not(test), no_std)]
#![doc = include_str!("../README.md")]
mod config;
mod de;
mod mqtt_client;
mod packets;
mod properties;
mod publication;
mod reason_codes;
mod ser;
mod types;
mod varint;
mod will;
mod wire;
pub use config::{Buffers, ConfigBuilder};
pub use mqtt_client::{ConnectEvent, InboundPublish, Io, Op, Session};
pub use packets::Disconnect;
pub use properties::{Properties, Property};
pub use publication::{OwnedResponseTarget, Publication, ToPayload};
pub use reason_codes::ReasonCode;
pub use types::{RetainHandling, SubscriptionOptions, TopicFilter};
pub use will::Will;
#[cfg(feature = "fuzzing")]
#[doc(hidden)]
pub mod fuzzing;
use de::Error as DeError;
use ser::{Error as SerError, PubError as SerPubError};
use num_enum::TryFromPrimitive;
#[cfg(feature = "defmt")]
pub(crate) use defmt::{debug, error, info, trace, warn};
#[cfg(not(feature = "defmt"))]
macro_rules! discard_log {
($message:literal $(, $arg:expr)* $(,)?) => {
{
let _ = $message;
$(let _ = &$arg;)*
}
};
}
#[cfg(not(feature = "defmt"))]
pub(crate) use discard_log as debug;
#[cfg(not(feature = "defmt"))]
pub(crate) use discard_log as error;
#[cfg(not(feature = "defmt"))]
pub(crate) use discard_log as info;
#[cfg(not(feature = "defmt"))]
pub(crate) use discard_log as trace;
#[cfg(not(feature = "defmt"))]
pub(crate) use discard_log as warn;
pub const MQTT_INSECURE_DEFAULT_PORT: u16 = 1883;
pub const MQTT_SECURE_DEFAULT_PORT: u16 = 8883;
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, PartialOrd, Ord)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum QoS {
AtMostOnce = 0,
AtLeastOnce = 1,
ExactlyOnce = 2,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub(crate) enum Retain {
NotRetained = 0,
Retained = 1,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum ConfigError {
#[error("buffer split exceeds backing storage")]
BufferSplit,
#[error("provided client ID is too long")]
ClientIdTooLong,
#[error("provided topic is too long")]
TopicTooLong,
#[error("configuration was specified more than once")]
DuplicateConfig,
#[error("invalid MQTT configuration")]
InvalidConfig,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum PeerError {
#[error("broker returned failure reason {0:?}")]
Rejected(ReasonCode),
#[error("received an invalid MQTT packet")]
InvalidPacket,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum ResourceError {
#[error("buffer is too small")]
BufferTooSmall,
#[error("packet is too large")]
PacketTooLarge,
#[error("in-flight metadata capacity exhausted")]
InflightExhausted,
}
#[derive(Debug, PartialEq, thiserror::Error)]
pub enum PubError<P, T> {
#[error(transparent)]
Session(#[from] Error<T>),
#[error("payload serialization failed")]
Payload(P),
}
impl<P, T> From<SerPubError<P>> for PubError<P, T> {
fn from(e: SerPubError<P>) -> Self {
match e {
SerPubError::Payload(e) => Self::Payload(e),
SerPubError::Encode(e) => Self::Session(Error::from(e)),
}
}
}
impl<P, T> From<ProtocolError> for PubError<P, T> {
fn from(err: ProtocolError) -> Self {
Self::Session(err.into())
}
}
#[derive(Debug, PartialEq, thiserror::Error)]
#[non_exhaustive]
pub enum Error<E> {
#[error("session is not ready")]
NotReady,
#[error("session is disconnected")]
Disconnected,
#[error("invalid request")]
InvalidRequest,
#[error(transparent)]
Peer(PeerError),
#[error(transparent)]
Resource(ResourceError),
#[error("transport error: {0:?}")]
Transport(E),
#[error("transport write returned zero bytes")]
WriteZero,
}
impl<E> From<ProtocolError> for Error<E> {
fn from(p: ProtocolError) -> Self {
match p {
ProtocolError::UnexpectedPacket
| ProtocolError::MalformedPacket
| ProtocolError::Deserialization(_) => Self::Peer(PeerError::InvalidPacket),
ProtocolError::InflightMetadataExhausted => {
Self::Resource(ResourceError::InflightExhausted)
}
ProtocolError::PacketTooLarge => Self::Resource(ResourceError::PacketTooLarge),
ProtocolError::Encode(err) => Self::from(err),
}
}
}
impl<E> From<SerError> for Error<E> {
fn from(err: SerError) -> Self {
match err {
SerError::InsufficientMemory => Self::Resource(ResourceError::BufferTooSmall),
SerError::Custom => Self::InvalidRequest,
}
}
}
impl<E> From<DeError> for Error<E> {
fn from(err: DeError) -> Self {
let _ = err;
Self::Peer(PeerError::InvalidPacket)
}
}
impl<E> From<PeerError> for Error<E> {
fn from(err: PeerError) -> Self {
Self::Peer(err)
}
}
impl<E> From<ResourceError> for Error<E> {
fn from(err: ResourceError) -> Self {
Self::Resource(err)
}
}
#[derive(Debug, Clone, PartialEq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) enum ProtocolError {
#[error("received an unexpected MQTT packet")]
UnexpectedPacket,
#[error("received a malformed MQTT packet")]
MalformedPacket,
#[error("in-flight metadata capacity exhausted")]
InflightMetadataExhausted,
#[error("required packet is too large")]
PacketTooLarge,
#[error(transparent)]
Encode(#[from] SerError),
#[error(transparent)]
Deserialization(#[from] DeError),
}
#[cfg(test)]
#[path = "../tests/support/mod.rs"]
pub(crate) mod tests;