use async_trait::async_trait;
use crate::{
envelope::{JsonlEnvelope, ReplayEnvelope, SseEnvelope},
error::{ReplayError, ReplayResult},
replay::{ReplayCursor, ReplayEvent, ReplayEventKind, ReplayEventLog, ReplayScope},
};
#[async_trait]
pub trait ReplayTransport: Send + Sync {
async fn replay(
&self,
scope: ReplayScope,
cursor: Option<ReplayCursor>,
) -> ReplayResult<Vec<ReplayEnvelope>>;
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ReplaySseFrame {
pub id: String,
pub event: String,
pub data: String,
}
impl ReplaySseFrame {
#[must_use]
pub fn ready() -> Self {
Self {
id: String::new(),
event: "ready".to_string(),
data: "{}".to_string(),
}
}
pub fn from_event(event: &ReplayEvent) -> ReplayResult<Self> {
Ok(Self {
id: event.sequence.to_string(),
event: replay_sse_event_name(&event.event).to_string(),
data: serde_json::to_string(event)
.map_err(|error| ReplayError::Failed(error.to_string()))?,
})
}
#[must_use]
pub fn to_frame(&self) -> String {
let id = if self.id.is_empty() {
String::new()
} else {
format!("id: {}\n", self.id)
};
format!("{id}event: {}\ndata: {}\n\n", self.event, self.data)
}
}
pub fn replay_sse_frames(events: &[ReplayEvent]) -> ReplayResult<Vec<ReplaySseFrame>> {
events.iter().map(ReplaySseFrame::from_event).collect()
}
#[must_use]
pub const fn replay_sse_event_name(kind: &ReplayEventKind) -> &'static str {
match kind {
ReplayEventKind::DisplayMessage(_) => "display_message",
ReplayEventKind::Raw(_) => "raw",
ReplayEventKind::Snapshot(_) => "snapshot",
ReplayEventKind::Heartbeat => "heartbeat",
ReplayEventKind::Terminal { .. } => "terminal",
}
}
pub struct InMemoryReplayTransport<L> {
log: L,
protocol: ReplayTransportProtocol,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ReplayTransportProtocol {
Sse,
Jsonl,
}
impl<L> InMemoryReplayTransport<L> {
#[must_use]
pub const fn sse(log: L) -> Self {
Self {
log,
protocol: ReplayTransportProtocol::Sse,
}
}
#[must_use]
pub const fn jsonl(log: L) -> Self {
Self {
log,
protocol: ReplayTransportProtocol::Jsonl,
}
}
}
#[async_trait]
impl<L> ReplayTransport for InMemoryReplayTransport<L>
where
L: ReplayEventLog,
{
async fn replay(
&self,
scope: ReplayScope,
cursor: Option<ReplayCursor>,
) -> ReplayResult<Vec<ReplayEnvelope>> {
let events = self.log.replay_after(&scope, cursor, None).await?;
Ok(events
.iter()
.map(|event| match self.protocol {
ReplayTransportProtocol::Sse => ReplayEnvelope::Sse(SseEnvelope::from_event(event)),
ReplayTransportProtocol::Jsonl => {
ReplayEnvelope::Jsonl(JsonlEnvelope::from_event(event))
}
})
.collect())
}
}