use std::time::Duration;
use crate::protocol::packet::MAX_PACKET_SIZE;
use crate::protocol::{MqttVersion, QoS};
use crate::support::secs_or;
pub(super) const DEFAULT_OPERATION_TIMEOUT_SECS: u32 = 15;
#[derive(Debug, Clone, PartialEq, Eq, uniffi::Record)]
pub struct WillOptions {
pub topic: String,
pub payload: Vec<u8>,
pub qos: QoS,
pub retain: bool,
}
#[derive(Clone, PartialEq, Eq, uniffi::Record)]
pub struct TlsOptions {
pub ca_cert_pem: Option<Vec<u8>>,
pub client_cert_pem: Option<Vec<u8>>,
pub client_key_pem: Option<Vec<u8>>,
pub insecure_skip_certificate_verification: bool,
}
crate::redacted_debug!(TlsOptions {
ca_cert_pem, client_cert_pem, insecure_skip_certificate_verification
} secret { client_key_pem });
impl TlsOptions {
pub fn new() -> Self {
TlsOptions {
ca_cert_pem: None,
client_cert_pem: None,
client_key_pem: None,
insecure_skip_certificate_verification: false,
}
}
}
impl Default for TlsOptions {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, PartialEq, Eq, uniffi::Record)]
pub struct ConnectOptions {
pub host: String,
pub port: u16,
pub client_id: String,
pub version: MqttVersion,
pub clean_start: bool,
pub keep_alive_secs: u16,
pub username: Option<String>,
pub password: Option<Vec<u8>>,
pub will: Option<WillOptions>,
pub connect_timeout_secs: u32,
pub operation_timeout_secs: u32,
pub auto_reconnect: bool,
pub reconnect_backoff_secs: u32,
pub reconnect_max_backoff_secs: u32,
pub tls: Option<TlsOptions>,
#[uniffi(default = 0)]
pub max_packet_size: u32,
}
crate::redacted_debug!(ConnectOptions {
host, port, client_id, version, clean_start, keep_alive_secs, username, will,
connect_timeout_secs, operation_timeout_secs, auto_reconnect,
reconnect_backoff_secs, reconnect_max_backoff_secs, tls, max_packet_size
} secret { password });
impl ConnectOptions {
pub fn new(
host: impl Into<String>,
port: u16,
client_id: impl Into<String>,
version: MqttVersion,
) -> Self {
ConnectOptions {
host: host.into(),
port,
client_id: client_id.into(),
version,
clean_start: true,
keep_alive_secs: 30,
username: None,
password: None,
will: None,
connect_timeout_secs: 10,
operation_timeout_secs: 0,
auto_reconnect: false,
reconnect_backoff_secs: 0,
reconnect_max_backoff_secs: 0,
tls: None,
max_packet_size: 0,
}
}
pub(super) fn operation_timeout(&self) -> Duration {
secs_or(self.operation_timeout_secs, DEFAULT_OPERATION_TIMEOUT_SECS)
}
pub(super) fn connect_timeout(&self) -> Duration {
secs_or(self.connect_timeout_secs, 1)
}
pub(super) fn initial_reconnect_backoff(&self) -> Duration {
secs_or(self.reconnect_backoff_secs, 1)
}
pub(super) fn max_reconnect_backoff(&self) -> Duration {
secs_or(self.reconnect_max_backoff_secs, 30)
}
pub(super) fn max_packet_size(&self) -> usize {
match self.max_packet_size {
0 => MAX_PACKET_SIZE,
n => n as usize,
}
}
pub(super) fn validate(&self) -> crate::error::MqttResult<()> {
use crate::error::MqttError::Protocol;
use crate::protocol::topic::is_valid_topic_name;
if self.host.is_empty() {
return Err(Protocol("ConnectOptions.host must not be empty".into()));
}
if self.client_id.len() > usize::from(u16::MAX) {
return Err(Protocol("ConnectOptions.client_id is too long".into()));
}
if self.password.is_some() && self.username.is_none() && !self.version.is_v5() {
return Err(Protocol(
"ConnectOptions.password requires a username in MQTT 3.1.1".into(),
));
}
if let Some(will) = &self.will {
if !is_valid_topic_name(&will.topic) {
return Err(Protocol(format!(
"invalid will topic {:?}: must be non-empty and wildcard-free",
will.topic
)));
}
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, uniffi::Record)]
pub struct ConnectResult {
pub session_present: bool,
pub reason_code: u8,
}
#[derive(Debug, Clone, PartialEq, Eq, uniffi::Record)]
pub struct MqttMessage {
pub topic: String,
pub payload: Vec<u8>,
pub qos: QoS,
pub retain: bool,
}
impl From<&crate::protocol::publish::PublishPacket> for MqttMessage {
fn from(p: &crate::protocol::publish::PublishPacket) -> Self {
MqttMessage {
topic: p.topic.clone(),
payload: p.payload.to_vec(),
qos: p.qos,
retain: p.retain,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, uniffi::Record)]
pub struct SubscribeResult {
pub reason_code: u8,
}
#[uniffi::export(with_foreign)]
pub trait MqttMessageListener: Send + Sync {
fn on_message(&self, message: MqttMessage);
fn on_disconnected(&self, reason: String);
}