use crate::line::LineDecoder;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SseEvent {
pub event: Option<String>,
pub data: String,
}
#[derive(Debug, Default)]
pub struct SseDecoder {
lines: LineDecoder,
event: Option<String>,
data: Vec<String>,
have_record: bool,
}
impl SseDecoder {
pub fn new() -> Self {
Self::default()
}
pub fn push(&mut self, bytes: &[u8]) -> Vec<SseEvent> {
let mut events = Vec::new();
for line in self.lines.push(bytes) {
let text = String::from_utf8_lossy(&line);
if let Some(event) = self.push_line(&text) {
events.push(event);
}
}
events
}
pub fn push_line(&mut self, line: &str) -> Option<SseEvent> {
if line.is_empty() {
return self.dispatch();
}
if line.starts_with(':') {
return 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.data.push(value.to_owned());
self.have_record = true;
}
_ => {}
}
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.have_record = false;
Some(SseEvent { event, data })
}
pub fn flush(&mut self) -> Option<SseEvent> {
if let Some(line) = self.lines.flush() {
let text = String::from_utf8_lossy(&line);
if let Some(event) = self.push_line(&text) {
return Some(event);
}
}
self.dispatch()
}
}