zai_rs/model/
sse_parser.rs1#[derive(Debug, Default)]
11pub struct SseEventParser {
12 buf: Vec<u8>,
13 event_data: Vec<Vec<u8>>,
14}
15
16impl SseEventParser {
17 pub fn new() -> Self {
18 Self::default()
19 }
20
21 pub fn push(&mut self, new_bytes: &[u8]) -> Vec<Vec<u8>> {
23 self.buf.extend_from_slice(new_bytes);
24 let mut events = Vec::new();
25
26 while let Some(newline_index) = self.buf.iter().position(|&b| b == b'\n') {
27 let mut line = self.buf.drain(..=newline_index).collect::<Vec<_>>();
28 if line.ends_with(b"\n") {
29 line.pop();
30 }
31 if line.ends_with(b"\r") {
32 line.pop();
33 }
34
35 if line.is_empty() {
36 if !self.event_data.is_empty() {
37 events.push(join_event_data(&self.event_data));
38 self.event_data.clear();
39 }
40 continue;
41 }
42
43 if line.starts_with(b":") {
44 continue;
45 }
46
47 if let Some(rest) = line.strip_prefix(b"data:") {
48 self.event_data.push(trim_one_leading_space(rest).to_vec());
49 }
50 }
51
52 events
53 }
54}
55
56fn trim_one_leading_space(bytes: &[u8]) -> &[u8] {
57 bytes.strip_prefix(b" ").unwrap_or(bytes)
58}
59
60fn join_event_data(lines: &[Vec<u8>]) -> Vec<u8> {
61 let mut event = Vec::new();
62 for (idx, line) in lines.iter().enumerate() {
63 if idx > 0 {
64 event.push(b'\n');
65 }
66 event.extend_from_slice(line);
67 }
68 event
69}
70
71pub fn extract_sse_data_lines(buf: &mut Vec<u8>, new_bytes: &[u8]) -> Vec<Vec<u8>> {
85 buf.extend_from_slice(new_bytes);
86 let mut results = Vec::new();
87
88 let Some(last_newline) = buf.iter().rposition(|&b| b == b'\n') else {
89 return results;
90 };
91
92 let completed = &buf[..=last_newline];
93 for line_with_nl in completed.split_inclusive(|&b| b == b'\n') {
94 let mut line = line_with_nl;
95 if let Some(line_without_nl) = line.strip_suffix(b"\n") {
96 line = line_without_nl;
97 }
98 if let Some(line_without_cr) = line.strip_suffix(b"\r") {
99 line = line_without_cr;
100 }
101 if line.is_empty() {
102 continue;
103 }
104 const PREFIX: &[u8] = b"data: ";
105 if let Some(rest) = line.strip_prefix(PREFIX) {
106 results.push(rest.to_vec());
107 }
108 }
109
110 buf.drain(..=last_newline);
111
112 results
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn test_single_complete_line() {
121 let mut buf = Vec::new();
122 let lines = extract_sse_data_lines(&mut buf, b"data: hello\n");
123 assert_eq!(lines.len(), 1);
124 assert_eq!(lines[0], b"hello");
125 }
126
127 #[test]
128 fn test_partial_then_complete() {
129 let mut buf = Vec::new();
130 let lines1 = extract_sse_data_lines(&mut buf, b"data: hel");
131 assert!(lines1.is_empty());
132
133 let lines2 = extract_sse_data_lines(&mut buf, b"lo\n");
134 assert_eq!(lines2.len(), 1);
135 assert_eq!(lines2[0], b"hello");
136 }
137
138 #[test]
139 fn test_crlf_line_endings() {
140 let mut buf = Vec::new();
141 let lines = extract_sse_data_lines(&mut buf, b"data: world\r\n");
142 assert_eq!(lines.len(), 1);
143 assert_eq!(lines[0], b"world");
144 }
145
146 #[test]
147 fn test_multiple_events_in_one_chunk() {
148 let mut buf = Vec::new();
149 let lines = extract_sse_data_lines(&mut buf, b"data: first\n\ndata: second\n");
150 assert_eq!(lines.len(), 2);
151 assert_eq!(lines[0], b"first");
152 assert_eq!(lines[1], b"second");
153 }
154
155 #[test]
156 fn test_done_marker() {
157 let mut buf = Vec::new();
158 let lines = extract_sse_data_lines(&mut buf, b"data: [DONE]\n");
159 assert_eq!(lines.len(), 1);
160 assert_eq!(lines[0], b"[DONE]");
161 }
162
163 #[test]
164 fn test_non_data_lines_skipped() {
165 let mut buf = Vec::new();
166 let lines = extract_sse_data_lines(&mut buf, b": comment\nid: 123\ndata: payload\n");
167 assert_eq!(lines.len(), 1);
168 assert_eq!(lines[0], b"payload");
169 }
170
171 #[test]
172 fn test_empty_lines_ignored() {
173 let mut buf = Vec::new();
174 let lines = extract_sse_data_lines(&mut buf, b"\n\n\ndata: hello\n\n");
175 assert_eq!(lines.len(), 1);
176 assert_eq!(lines[0], b"hello");
177 }
178
179 #[test]
180 fn event_parser_yields_complete_events() {
181 let mut parser = SseEventParser::new();
182 assert!(parser.push(b"data: hel").is_empty());
183 assert_eq!(parser.push(b"lo\r\n\r\n"), vec![b"hello".to_vec()]);
184 }
185
186 #[test]
187 fn event_parser_joins_multi_data_lines() {
188 let mut parser = SseEventParser::new();
189 let events = parser.push(b"data: {\"a\":\ndata: 1}\n\n");
190 assert_eq!(events, vec![b"{\"a\":\n1}".to_vec()]);
191 }
192
193 #[test]
194 fn event_parser_ignores_comments_and_non_data_fields() {
195 let mut parser = SseEventParser::new();
196 let events = parser.push(b": keepalive\nid: 1\ndata: payload\n\n");
197 assert_eq!(events, vec![b"payload".to_vec()]);
198 }
199}