#![cfg_attr(not(test), no_std)]
#![doc = include_str!("../README.md")]
pub mod config;
mod de;
mod message_types;
mod mqtt_client;
mod packets;
mod properties;
pub mod publication;
mod reason_codes;
mod ser;
pub mod types;
mod varint;
mod will;
pub use config::{Buffers, ConfigBuilder};
pub use mqtt_client::{ConnectEvent, InboundPublish, Io, Op, OpStatus, Session};
pub use properties::Property;
pub use publication::{OwnedResponseTarget, Publication};
pub use reason_codes::ReasonCode;
pub use will::Will;
#[cfg(feature = "fuzzing")]
#[doc(hidden)]
pub mod fuzzing;
use de::Error as DeError;
use ser::Error as SerError;
use num_enum::TryFromPrimitive;
pub(crate) use defmt::{debug, error, info, trace, warn};
pub type SessionError<IO> = Error<<IO as embedded_io_async::ErrorType>::Error>;
pub type PublishError<IO, P> = PubError<P, <IO as embedded_io_async::ErrorType>::Error>;
pub const MQTT_INSECURE_DEFAULT_PORT: u16 = 1883;
pub const MQTT_SECURE_DEFAULT_PORT: u16 = 8883;
pub const TOPIC_CAPACITY: usize = 128;
pub type TopicString = heapless::String<TOPIC_CAPACITY>;
#[derive(defmt::Format, Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, PartialOrd, Ord)]
#[repr(u8)]
pub enum QoS {
AtMostOnce = 0,
AtLeastOnce = 1,
ExactlyOnce = 2,
}
#[derive(defmt::Format, Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive)]
#[repr(u8)]
pub enum Retain {
NotRetained = 0,
Retained = 1,
}
#[derive(defmt::Format, Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum ConfigError {
#[error("buffer split exceeds backing storage")]
BufferSplit,
#[error("provided client ID is too long")]
ClientIdTooLong,
#[error("configuration was specified more than once")]
DuplicateConfig,
#[error("invalid MQTT configuration")]
InvalidConfig,
}
#[derive(defmt::Format, Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum PeerError {
#[error("broker returned failure reason {0:?}")]
Rejected(ReasonCode),
#[error("received an invalid MQTT packet")]
InvalidPacket,
}
#[derive(defmt::Format, Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
#[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, E> {
#[error(transparent)]
Session(#[from] Error<E>),
#[error("payload serialization failed")]
Payload(P),
}
impl<P, E> From<crate::ser::PubError<P>> for PubError<P, E> {
fn from(e: crate::ser::PubError<P>) -> Self {
match e {
crate::ser::PubError::Payload(e) => Self::Payload(e),
crate::ser::PubError::Encode(e) => Self::Session(Error::from(e)),
}
}
}
impl<P, E> From<ProtocolError> for PubError<P, E> {
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::Failed(code) => match code {
ReasonCode::PacketTooLarge => Self::Resource(ResourceError::PacketTooLarge),
code => Self::Peer(PeerError::Rejected(code)),
},
ProtocolError::Encode(err) => Self::from(err),
}
}
}
impl<E> From<crate::ser::Error> for Error<E> {
fn from(err: crate::ser::Error) -> Self {
match err {
crate::ser::Error::InsufficientMemory => Self::Resource(ResourceError::BufferTooSmall),
crate::ser::Error::Custom => Self::InvalidRequest,
}
}
}
impl<E> From<crate::de::Error> for Error<E> {
fn from(err: crate::de::Error) -> 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(defmt::Format, Debug, Clone, PartialEq, thiserror::Error)]
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("broker returned failure reason {0:?}")]
Failed(ReasonCode),
#[error(transparent)]
Encode(#[from] SerError),
#[error(transparent)]
Deserialization(#[from] DeError),
}
impl From<ReasonCode> for ProtocolError {
fn from(code: ReasonCode) -> Self {
Self::Failed(code)
}
}
#[cfg(test)]
#[path = "../tests/support/mod.rs"]
pub(crate) mod tests;