use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, AtomicU16, Ordering};
use std::sync::Arc;
use std::time::Duration;
use tokio::io::WriteHalf;
use tokio::sync::{oneshot, Mutex as AsyncMutex};
use tokio::task::JoinHandle;
use crate::error::{MqttError, MqttResult};
use crate::protocol::packet::Packet;
use crate::protocol::MqttVersion;
use super::tls::Transport;
use super::types::{MqttMessage, MqttMessageListener};
pub(super) struct PendingQos2Incoming {
pub messages: HashMap<u16, MqttMessage>,
}
pub(super) struct Inner {
pub version: MqttVersion,
pub writer: AsyncMutex<Option<WriteHalf<Box<dyn Transport>>>>,
next_packet_id: AtomicU16,
pub pending: std::sync::Mutex<HashMap<u16, oneshot::Sender<Packet>>>,
pub incoming_qos2: std::sync::Mutex<PendingQos2Incoming>,
pub listener: std::sync::Mutex<Option<Arc<dyn MqttMessageListener>>>,
pub connected: AtomicBool,
pub read_task: AsyncMutex<Option<JoinHandle<()>>>,
pub keepalive_task: AsyncMutex<Option<JoinHandle<()>>>,
pub operation_timeout: Duration,
}
impl Inner {
pub fn new(
version: MqttVersion,
writer: WriteHalf<Box<dyn Transport>>,
listener: Option<Arc<dyn MqttMessageListener>>,
operation_timeout: Duration,
) -> Self {
Inner {
version,
writer: AsyncMutex::new(Some(writer)),
next_packet_id: AtomicU16::new(1),
pending: std::sync::Mutex::new(HashMap::new()),
incoming_qos2: std::sync::Mutex::new(PendingQos2Incoming {
messages: HashMap::new(),
}),
listener: std::sync::Mutex::new(listener),
connected: AtomicBool::new(true),
read_task: AsyncMutex::new(None),
keepalive_task: AsyncMutex::new(None),
operation_timeout,
}
}
pub fn alloc_packet_id(&self) -> u16 {
loop {
let id = self.next_packet_id.fetch_add(1, Ordering::Relaxed);
if id != 0 {
return id;
}
}
}
pub fn take_listener(&self) -> Option<Arc<dyn MqttMessageListener>> {
self.listener.lock().unwrap().clone()
}
}
pub(super) fn register_pending(inner: &Arc<Inner>, id: u16) -> oneshot::Receiver<Packet> {
let (tx, rx) = oneshot::channel();
inner.pending.lock().unwrap().insert(id, tx);
rx
}
pub(super) fn resolve_pending(inner: &Arc<Inner>, id: u16, packet: Packet) {
if let Some(tx) = inner.pending.lock().unwrap().remove(&id) {
let _ = tx.send(packet);
}
}
pub(super) async fn wait_for(
inner: &Arc<Inner>,
rx: oneshot::Receiver<Packet>,
) -> MqttResult<Packet> {
match tokio::time::timeout(inner.operation_timeout, rx).await {
Ok(Ok(packet)) => Ok(packet),
Ok(Err(_)) => Err(MqttError::Session(
"connection closed while waiting for ack".into(),
)),
Err(_) => Err(MqttError::Timeout),
}
}
pub(super) fn unexpected(packet: &Packet) -> MqttError {
MqttError::MalformedPacket(format!("unexpected packet while awaiting ack: {packet:?}"))
}
pub(super) fn deliver(inner: &Arc<Inner>, message: MqttMessage) {
if let Some(listener) = inner.take_listener() {
listener.on_message(message);
}
}
pub(super) fn finish_disconnected(inner: &Arc<Inner>, reason: String) {
inner.connected.store(false, Ordering::Relaxed);
if let Some(listener) = inner.take_listener() {
listener.on_disconnected(reason);
}
}