1#[derive(Clone, Debug, Default, PartialEq, Eq)]
5pub struct Event {
6 pub event: Option<String>,
8 pub data: String,
10}
11
12#[derive(Debug, Default)]
18pub struct Decoder {
19 pending: Vec<u8>,
20 buffer: String,
21 event: Option<String>,
22 data: Vec<String>,
23}
24
25impl Decoder {
26 pub fn push(&mut self, chunk: &[u8]) -> Vec<Event> {
28 let text = yapi_types::js::decode_utf8_stream(&mut self.pending, chunk);
29 self.buffer.push_str(&text);
30 self.lines()
31 }
32
33 pub fn finish(&mut self) -> Vec<Event> {
36 if !self.pending.is_empty() {
37 let text = String::from_utf8_lossy(&self.pending).into_owned();
38 self.pending.clear();
39 self.buffer.push_str(&text);
40 }
41 let mut events = self.lines();
42 if !self.buffer.is_empty() {
43 let line = std::mem::take(&mut self.buffer);
44 events.extend(self.line(&line));
45 }
46 events.extend(self.flush());
47 events
48 }
49
50 fn lines(&mut self) -> Vec<Event> {
51 let mut events = Vec::new();
52 while let Some(index) = self.buffer.find(['\r', '\n']) {
53 let mut next = index + 1;
54 if self.buffer.as_bytes()[index] == b'\r' {
56 match self.buffer.as_bytes().get(next) {
57 Some(b'\n') => next += 1,
58 None => break,
59 Some(_) => {}
60 }
61 }
62 let line = self.buffer[..index].to_owned();
63 self.buffer.drain(..next);
64 events.extend(self.line(&line));
65 }
66 events
67 }
68
69 fn line(&mut self, line: &str) -> Option<Event> {
70 if line.is_empty() {
71 return self.flush();
72 }
73 if line.starts_with(':') {
74 return None;
75 }
76 let (field, value) = match line.split_once(':') {
77 Some((field, value)) => (field, value.strip_prefix(' ').unwrap_or(value)),
78 None => (line, ""),
79 };
80 match field {
81 "event" => self.event = Some(value.to_owned()),
82 "data" => self.data.push(value.to_owned()),
83 _ => {}
84 }
85 None
86 }
87
88 fn flush(&mut self) -> Option<Event> {
89 if self.event.is_none() && self.data.is_empty() {
90 return None;
91 }
92 Some(Event {
93 event: self.event.take(),
94 data: std::mem::take(&mut self.data).join("\n"),
95 })
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 fn decode(chunks: &[&[u8]]) -> Vec<Event> {
104 let mut decoder = Decoder::default();
105 let mut events = Vec::new();
106 for chunk in chunks {
107 events.extend(decoder.push(chunk));
108 }
109 events.extend(decoder.finish());
110 events
111 }
112
113 fn event(name: Option<&str>, data: &str) -> Event {
114 Event {
115 event: name.map(str::to_owned),
116 data: data.to_owned(),
117 }
118 }
119
120 #[test]
121 fn splits_events_across_chunks() {
122 let events = decode(&[
123 b"event: a\nda",
124 b"ta: {\"x\":1}\n\nevent: b\r\ndata: 2\r",
125 b"\n\r\n",
126 ]);
127 assert_eq!(
128 events,
129 [event(Some("a"), "{\"x\":1}"), event(Some("b"), "2")]
130 );
131 }
132
133 #[test]
134 fn joins_data_skips_comments() {
135 let events = decode(&[b": ping\ndata: one\ndata:two\nid: 3\n\n"]);
136 assert_eq!(events, [event(None, "one\ntwo")]);
137 }
138
139 #[test]
140 fn split_utf8_and_flush() {
141 let crab = "data: 🦀".as_bytes();
142 let events = decode(&[&crab[..8], &crab[8..]]);
143 assert_eq!(events, [event(None, "🦀")]);
144 }
145}