use std::fmt::Debug;
use chrono::{DateTime, FixedOffset};
use serde::{de::DeserializeOwned, Deserialize, Serialize, Serializer};
use url::Url;
use crate::types::{ConduitId, SessionId};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum TransportMethod {
Webhook,
Websocket,
Conduit,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Transport {
pub method: TransportMethod,
#[serde(skip_serializing_if = "Option::is_none")]
pub callback: Option<Url>,
#[serde(skip_serializing_if = "Option::is_none")]
pub secret: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub session_id: Option<SessionId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub conduit_id: Option<ConduitId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub connected_at: Option<DateTime<FixedOffset>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub disconnected_at: Option<DateTime<FixedOffset>>,
}
impl Transport {
pub fn websocket(session_id: SessionId) -> WebsocketTransport {
WebsocketTransport::new(session_id)
}
pub fn webhook<T: Into<String>>(callback: Url, secret: T) -> WebhookTransport {
WebhookTransport::new(callback, secret.into())
}
pub fn conduit(conduit_id: ConduitId) -> ConduitTransport {
ConduitTransport::new(conduit_id)
}
}
mod sealed {
pub trait Sealed {}
}
pub trait TransportType: sealed::Sealed + Debug + Sized + Serialize + DeserializeOwned {
fn method_name(&self) -> &'static str;
fn into_json(self) -> Option<String> {
Some(serde_json::to_string(&self).unwrap())
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct WebhookTransport {
pub callback: Url,
pub secret: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct WebsocketTransport {
pub session_id: SessionId,
#[serde(skip_serializing_if = "Option::is_none")]
pub connected_at: Option<DateTime<FixedOffset>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub disconnected_at: Option<DateTime<FixedOffset>>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ConduitTransport {
pub conduit_id: ConduitId,
}
impl sealed::Sealed for WebhookTransport {}
impl sealed::Sealed for WebsocketTransport {}
impl sealed::Sealed for ConduitTransport {}
impl TransportType for WebhookTransport {
fn method_name(&self) -> &'static str {
"webhook"
}
}
impl TransportType for WebsocketTransport {
fn method_name(&self) -> &'static str {
"websocket"
}
}
impl TransportType for ConduitTransport {
fn method_name(&self) -> &'static str {
"conduit"
}
}
impl WebhookTransport {
pub fn new(callback: Url, secret: impl Into<String>) -> Self {
Self {
callback,
secret: Some(secret.into()),
}
}
}
impl WebsocketTransport {
pub fn new(session_id: SessionId) -> Self {
Self {
session_id,
connected_at: None,
disconnected_at: None,
}
}
pub fn set_connected_at(&mut self, connected_at: DateTime<FixedOffset>) {
self.connected_at = Some(connected_at);
}
}
impl ConduitTransport {
pub fn new(conduit_id: ConduitId) -> Self {
Self { conduit_id }
}
}
impl Serialize for WebhookTransport {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
use serde::ser::SerializeStruct;
let mut state = serializer.serialize_struct("WebhookTransport", 3)?;
state.serialize_field("method", "webhook")?;
state.serialize_field("callback", &self.callback)?;
state.serialize_field("secret", &self.secret)?;
state.end()
}
}
impl Serialize for WebsocketTransport {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
use serde::ser::SerializeStruct;
let mut field_count = 1;
if self.connected_at.is_some() {
field_count += 1;
}
if self.disconnected_at.is_some() {
field_count += 1;
}
let mut state = serializer.serialize_struct("WebsocketTransport", field_count)?;
state.serialize_field("method", "websocket")?;
state.serialize_field("session_id", &self.session_id)?;
if let Some(connected_at) = &self.connected_at {
state.serialize_field("connected_at", connected_at)?;
}
if let Some(disconnected_at) = &self.disconnected_at {
state.serialize_field("disconnected_at", disconnected_at)?;
}
state.end()
}
}
impl Serialize for ConduitTransport {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
use serde::ser::SerializeStruct;
let mut state = serializer.serialize_struct("WebhookTransport", 1)?;
state.serialize_field("method", "conduit")?;
state.serialize_field("conduit_id", &self.conduit_id)?;
state.end()
}
}