use std::sync::OnceLock; use std::sync::atomic::{AtomicU64, Ordering}; use tokio::sync::mpsc; use uuid::Uuid; use crate::socket::packet::Packet; pub struct Socket { pub sid: String, pub namespace: String, pub engine_sid: String, /// Authenticated user ID, set once during `on_connect`. user_id: OnceLock, ack_id: AtomicU64, tx: mpsc::Sender, } #[allow(clippy::result_large_err)] impl Socket { pub fn new( sid: String, namespace: String, engine_sid: String, tx: mpsc::Sender, ) -> Self { Self { sid, namespace, engine_sid, ack_id: AtomicU64::new(0), user_id: OnceLock::new(), tx, } } /// Set the authenticated user ID after JWT verification. /// Safe to call once; subsequent calls are ignored. pub fn set_user_id(&self, id: Uuid) { let _ = self.user_id.set(id); } /// Get the authenticated user ID, if set. pub fn user_id(&self) -> Option { self.user_id.get().copied() } pub fn next_ack_id(&self) -> u64 { self.ack_id.fetch_add(1, Ordering::SeqCst) } pub fn send_packet(&self, packet: &Packet) -> Result<(), mpsc::error::TrySendError> { self.tx.try_send(packet.clone()) } pub fn emit( &self, event: impl Into, data: serde_json::Value, ) -> Result<(), mpsc::error::TrySendError> { let packet = Packet::event( &self.namespace, serde_json::json!([event.into(), data]), None, ); self.send_packet(&packet) } pub fn emit_with_ack( &self, event: impl Into, data: serde_json::Value, ) -> Result> { let ack_id = self.next_ack_id(); let packet = Packet::event( &self.namespace, serde_json::json!([event.into(), data]), Some(ack_id), ); self.send_packet(&packet)?; Ok(ack_id) } pub fn disconnect(&self) -> Result<(), mpsc::error::TrySendError> { let packet = Packet::disconnect(&self.namespace); self.send_packet(&packet) } pub fn send_ack( &self, id: u64, data: serde_json::Value, ) -> Result<(), mpsc::error::TrySendError> { let packet = Packet::ack(&self.namespace, data, id); self.send_packet(&packet) } }