1use std::time::Duration;
2
3use bytes::{BufMut, Bytes, BytesMut};
4use bytestring::ByteString;
5
6use crate::Data;
7
8#[must_use]
10#[derive(Debug, Clone)]
11pub enum Event {
12 Data(Data),
25
26 Comment(ByteString),
35}
36
37impl Event {
38 pub(crate) fn line_split_with_prefix(
40 buf: &mut BytesMut,
41 prefix: &'static str,
42 data: ByteString,
43 ) {
44 buf.reserve(data.len() + (10 * (prefix.len() + 1)) + 1);
46
47 for line in data.split('\n') {
49 buf.put_slice(prefix.as_bytes());
50 buf.put_slice(line.as_bytes());
51 buf.put_u8(b'\n');
52 }
53 }
54
55 pub(crate) fn into_bytes(self) -> Bytes {
57 let mut buf = BytesMut::new();
58
59 match self {
60 Event::Data(Data { id, event, data }) => {
61 if let Some(text) = id {
62 buf.put_slice(b"id: ");
63 buf.put_slice(text.as_bytes());
64 buf.put_u8(b'\n');
65 }
66
67 if let Some(text) = event {
68 buf.put_slice(b"event: ");
69 buf.put_slice(text.as_bytes());
70 buf.put_u8(b'\n');
71 }
72
73 Self::line_split_with_prefix(&mut buf, "data: ", data);
74 }
75
76 Event::Comment(text) => Self::line_split_with_prefix(&mut buf, ": ", text),
77 }
78
79 buf.put_u8(b'\n');
81
82 buf.freeze()
83 }
84
85 pub(crate) fn retry_to_bytes(retry: Duration) -> Bytes {
87 Bytes::from(format!("retry: {}\n\n", retry.as_millis()))
88 }
89
90 pub(crate) const fn keep_alive_bytes() -> Bytes {
92 Bytes::from_static(b": keep-alive\n\n")
93 }
94}
95
96impl From<Data> for Event {
97 fn from(data: Data) -> Self {
98 Self::Data(data)
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn format_retry_message() {
108 assert_eq!(
109 Event::retry_to_bytes(Duration::from_millis(1)),
110 "retry: 1\n\n",
111 );
112 assert_eq!(
113 Event::retry_to_bytes(Duration::from_secs(10)),
114 "retry: 10000\n\n",
115 );
116 }
117
118 #[test]
119 fn line_split_format() {
120 let mut buf = BytesMut::new();
121 Event::line_split_with_prefix(&mut buf, "data: ", ByteString::from("foo"));
122 assert_eq!(buf, "data: foo\n");
123
124 let mut buf = BytesMut::new();
125 Event::line_split_with_prefix(&mut buf, "data: ", ByteString::from("foo\nbar"));
126 assert_eq!(buf, "data: foo\ndata: bar\n");
127 }
128
129 #[test]
130 fn into_bytes_format() {
131 assert_eq!(Event::Comment("foo".into()).into_bytes(), ": foo\n\n");
132
133 assert_eq!(
134 Event::Data(Data {
135 id: None,
136 event: None,
137 data: "foo".into()
138 })
139 .into_bytes(),
140 "data: foo\n\n"
141 );
142
143 assert_eq!(
144 Event::Data(Data {
145 id: None,
146 event: None,
147 data: "\n".into()
148 })
149 .into_bytes(),
150 "data: \ndata: \n\n"
151 );
152
153 assert_eq!(
154 Event::Data(Data {
155 id: Some("42".into()),
156 event: None,
157 data: "foo".into()
158 })
159 .into_bytes(),
160 "id: 42\ndata: foo\n\n"
161 );
162
163 assert_eq!(
164 Event::Data(Data {
165 id: None,
166 event: Some("bar".into()),
167 data: "foo".into()
168 })
169 .into_bytes(),
170 "event: bar\ndata: foo\n\n"
171 );
172
173 assert_eq!(
174 Event::Data(Data {
175 id: Some("42".into()),
176 event: Some("bar".into()),
177 data: "foo".into()
178 })
179 .into_bytes(),
180 "id: 42\nevent: bar\ndata: foo\n\n"
181 );
182 }
183}