use crate::{
NetError,
line::{DEFAULT_MAX_LINE_BYTES, LineDecoder},
};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SseEvent {
pub event: Option<String>,
pub data: String,
}
#[derive(Debug)]
pub struct SseDecoder {
lines: LineDecoder,
event: Option<String>,
data: Vec<String>,
data_bytes: usize,
max_line_bytes: Option<usize>,
have_record: bool,
}
impl Default for SseDecoder {
fn default() -> Self {
Self::new()
}
}
impl SseDecoder {
pub fn new() -> Self {
Self::with_max_line_bytes(DEFAULT_MAX_LINE_BYTES)
}
pub fn unbounded() -> Self {
Self {
lines: LineDecoder::unbounded(),
event: None,
data: Vec::new(),
data_bytes: 0,
max_line_bytes: None,
have_record: false,
}
}
pub fn with_max_line_bytes(max_line_bytes: usize) -> Self {
Self {
lines: LineDecoder::with_max_line_bytes(max_line_bytes),
event: None,
data: Vec::new(),
data_bytes: 0,
max_line_bytes: Some(max_line_bytes),
have_record: false,
}
}
pub fn push(&mut self, bytes: &[u8]) -> Result<Vec<SseEvent>, NetError> {
self.push_checked(bytes)
}
pub fn push_checked(&mut self, bytes: &[u8]) -> Result<Vec<SseEvent>, NetError> {
let mut events = Vec::new();
for line in self.lines.push_checked(bytes)? {
let text = String::from_utf8_lossy(&line);
if let Some(event) = self.push_line_checked(&text)? {
events.push(event);
}
}
Ok(events)
}
pub fn push_line(&mut self, line: &str) -> Option<SseEvent> {
self.push_line_checked(line)
.expect("sse decoder cap exceeded; use push_line_checked for fallible handling")
}
pub fn push_line_checked(&mut self, line: &str) -> Result<Option<SseEvent>, NetError> {
self.check_line(line.len())?;
if line.is_empty() {
return Ok(self.dispatch());
}
if line.starts_with(':') {
return Ok(None);
}
let (field, value) = match line.split_once(':') {
Some((field, rest)) => (field, rest.strip_prefix(' ').unwrap_or(rest)),
None => (line, ""),
};
match field {
"event" => {
self.event = Some(value.to_owned());
self.have_record = true;
}
"data" => {
self.add_data(value)?;
self.data.push(value.to_owned());
self.have_record = true;
}
_ => {}
}
Ok(None)
}
fn dispatch(&mut self) -> Option<SseEvent> {
if !self.have_record {
return None;
}
let event = self.event.take();
let data = std::mem::take(&mut self.data).join("\n");
self.data_bytes = 0;
self.have_record = false;
Some(SseEvent { event, data })
}
pub fn flush(&mut self) -> Option<SseEvent> {
self.flush_checked()
.expect("sse decoder cap exceeded; use flush_checked for fallible handling")
}
pub fn flush_checked(&mut self) -> Result<Option<SseEvent>, NetError> {
if let Some(line) = self.lines.flush_checked()? {
let text = String::from_utf8_lossy(&line);
if let Some(event) = self.push_line_checked(&text)? {
return Ok(Some(event));
}
}
Ok(self.dispatch())
}
fn add_data(&mut self, value: &str) -> Result<(), NetError> {
let separator = usize::from(!self.data.is_empty());
let next_len = self
.data_bytes
.saturating_add(separator)
.saturating_add(value.len());
self.check_line(next_len)?;
self.data_bytes = next_len;
Ok(())
}
fn check_line(&self, len: usize) -> Result<(), NetError> {
if let Some(max) = self.max_line_bytes
&& len > max
{
return Err(NetError::LineTooLong { max });
}
Ok(())
}
}