use super::error::SocketError;
use crate::runtimes::http::SocketIoParser;
use axum::http::Extensions as HttpExtensions;
use bytes::Bytes;
use parking_lot::RwLock;
use serde::{Serialize, de::DeserializeOwned};
use socketioxide::{
ProtocolVersion, SendError, TransportType,
adapter::{Adapter, LocalAdapter},
extensions::Extensions,
extract::{AckSender, Event, SocketRef},
handler::{FromConnectParts, FromDisconnectParts, FromMessageParts},
socket::{DisconnectReason, Socket},
};
use socketioxide_core::{
Sid, Value,
parser::{Parse, ParseError},
};
use std::{convert::Infallible, sync::Arc};
#[cfg(feature = "validation-validator")]
use validator::Validate;
pub struct SocketContext<A: Adapter = LocalAdapter> {
pub socket: SocketRef<A>,
data: RwLock<Option<Value>>,
ack: Option<AckSender<A>>,
disconnect_reason: Option<DisconnectReason>,
event: Option<Box<str>>,
}
impl<A> SocketContext<A>
where
A: Adapter,
{
fn parser(&self) -> SocketIoParser {
self.socket
.req_parts()
.extensions
.get::<SocketIoParser>()
.cloned()
.unwrap_or_default()
}
pub fn try_data<T: DeserializeOwned>(&self) -> Result<T, SocketError> {
let mut data = self.data.write().take().ok_or(ParseError::InvalidData)?;
let result: T = match self.parser() {
SocketIoParser::Common(parser) => parser.decode_value(&mut data, true),
SocketIoParser::MsgPack(parser) => parser.decode_value(&mut data, true),
}
.map_err(SocketError::from)?;
Ok(result)
}
#[cfg(feature = "validation-validator")]
pub fn try_validated_data<T>(&self) -> Result<T, SocketError>
where
T: DeserializeOwned + Validate,
{
let data: T = self.try_data()?;
data.validate()?;
Ok(data)
}
pub fn event(&self) -> Option<&str> {
self.event.as_deref()
}
pub fn ack<D>(self, data: &D) -> Result<(), SendError>
where
D: Serialize + ?Sized,
{
let Some(ack) = self.ack else {
return Err(SendError::Socket(socketioxide::SocketError::Closed));
};
ack.send(data)?;
Ok(())
}
pub fn id(&self) -> &Sid {
&self.socket.id
}
pub fn has_ack(&self) -> bool {
self.ack.is_some()
}
pub fn has_data(&self) -> bool {
self.data.read().is_some()
}
pub fn extensions(&self) -> &Extensions {
&self.socket.extensions
}
pub fn http_extensions(&self) -> &HttpExtensions {
&self.socket.req_parts().extensions
}
pub fn protocol_version(&self) -> ProtocolVersion {
self.socket.protocol()
}
pub fn transport_type(&self) -> TransportType {
self.socket.transport_type()
}
pub fn disconnect(self) -> Result<(), SocketError> {
self.socket.disconnect().map_err(SocketError::from)
}
pub fn disconnect_reason(&self) -> Option<&DisconnectReason> {
self.disconnect_reason.as_ref()
}
}
impl<A> FromMessageParts<A> for SocketContext<A>
where
A: Adapter,
{
type Error = Infallible;
fn from_message_parts(
s: &Arc<Socket<A>>,
v: &mut Value,
ack_id: &Option<i64>,
) -> Result<Self, Self::Error> {
let ack = ack_id
.and_then(|id| AckSender::from_message_parts(s, v, &Some(id)).ok());
let event = Event::from_message_parts(s, v, ack_id)
.ok()
.map(|e| e.0.into_boxed_str());
let data = std::mem::replace(v, Value::Bytes(Bytes::new()));
let socket_ref = SocketRef::from_message_parts(s, v, ack_id)?;
Ok(SocketContext {
socket: socket_ref,
data: RwLock::new(Some(data)),
ack,
disconnect_reason: None,
event,
})
}
}
impl<A> FromConnectParts<A> for SocketContext<A>
where
A: Adapter,
{
type Error = Infallible;
fn from_connect_parts(
s: &Arc<Socket<A>>,
auth: &Option<Value>,
) -> Result<Self, Self::Error> {
Ok(SocketContext {
socket: SocketRef::from_connect_parts(s, auth)?,
data: RwLock::new(auth.clone()),
ack: None,
disconnect_reason: None,
event: None,
})
}
}
impl<A> FromDisconnectParts<A> for SocketContext<A>
where
A: Adapter,
{
type Error = Infallible;
fn from_disconnect_parts(
s: &Arc<Socket<A>>,
reason: DisconnectReason,
) -> Result<Self, Self::Error> {
Ok(SocketContext {
socket: SocketRef::from_disconnect_parts(s, reason)?,
data: RwLock::new(None),
ack: None,
disconnect_reason: Some(reason),
event: None,
})
}
}