use alloc::string::String;
use core::fmt;
use core::pin::Pin;
use core::task::{Context, Poll};
use core::time::Duration;
use futures_core::stream::{FusedStream, Stream};
use crate::{Event, SseParser};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Error<E> {
Transport(E),
}
impl<E: fmt::Display> fmt::Display for Error<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Transport(inner) => write!(f, "transport error: {inner}"),
}
}
}
#[cfg(feature = "std")]
impl<E: std::error::Error + 'static> std::error::Error for Error<E> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Transport(inner) => Some(inner),
}
}
}
pin_project_lite::pin_project! {
pub struct EventStream<S> {
#[pin]
inner: S,
parser: SseParser,
done: bool,
}
}
impl<S> EventStream<S> {
pub fn new(stream: S) -> Self {
EventStream {
inner: stream,
parser: SseParser::new(),
done: false,
}
}
pub fn set_last_event_id(&mut self, id: impl Into<String>) {
self.parser.set_last_event_id(id);
}
pub fn last_event_id(&self) -> &str {
self.parser.last_event_id()
}
pub fn retry(&self) -> Option<Duration> {
self.parser.retry()
}
}
impl<S: fmt::Debug> fmt::Debug for EventStream<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventStream")
.field("inner", &self.inner)
.field("done", &self.done)
.field("last_event_id", &self.parser.last_event_id())
.field("retry", &self.parser.retry())
.field("buffered_bytes", &self.parser.stats().buffered_bytes)
.finish_non_exhaustive()
}
}
impl<S, B, E> Stream for EventStream<S>
where
S: Stream<Item = Result<B, E>>,
B: AsRef<[u8]>,
{
type Item = Result<Event, Error<E>>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut this = self.project();
loop {
if let Some(event) = this.parser.next_event() {
return Poll::Ready(Some(Ok(event)));
}
if *this.done {
return Poll::Ready(None);
}
match this.inner.as_mut().poll_next(cx) {
Poll::Pending => return Poll::Pending,
Poll::Ready(Some(Ok(chunk))) => this.parser.feed(chunk.as_ref()),
Poll::Ready(Some(Err(e))) => return Poll::Ready(Some(Err(Error::Transport(e)))),
Poll::Ready(None) => {
*this.done = true;
this.parser.finish();
}
}
}
}
}
impl<S, B, E> FusedStream for EventStream<S>
where
S: Stream<Item = Result<B, E>>,
B: AsRef<[u8]>,
{
fn is_terminated(&self) -> bool {
self.done && !self.parser.has_pending_events()
}
}