1use std::io::{self, Write};
6use thiserror::Error;
7
8#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct Event {
11 event_type: String,
12 data: String,
13}
14
15impl Event {
16 pub fn from_type_and_data(
17 event_type: impl Into<String>,
18 data: impl Into<String>,
19 ) -> Result<Self, EventError> {
20 let event_type = event_type.into();
21 if event_type.contains('\r') {
22 return Err(EventError::TypeHasCarriageReturn);
23 }
24 if event_type.contains('\n') {
25 return Err(EventError::TypeHasNewline);
26 }
27 let data = data.into();
28 if data.is_empty() {
29 return Err(EventError::DataIsEmpty);
30 }
31 if data.contains('\r') {
32 return Err(EventError::DataHasCarriageReturn);
33 }
34 Ok(Self { event_type, data })
35 }
36
37 pub fn to_writer(&self, mut writer: impl Write) -> io::Result<()> {
39 if !self.event_type.is_empty() {
40 write!(&mut writer, "event: {}\n", self.event_type)?;
41 }
42 for line in self.data.split('\n') {
43 write!(&mut writer, "data: {}\n", line)?;
44 }
45 writer.write_all(b"\n")?;
46 Ok(())
47 }
48
49 pub fn to_vec(&self) -> Vec<u8> {
51 let mut ret = vec![];
52 self.to_writer(&mut ret).unwrap();
54 ret
55 }
56
57 pub fn event_type(&self) -> &str {
58 &self.event_type
59 }
60
61 pub fn data(&self) -> &str {
62 &self.data
63 }
64}
65
66#[derive(Debug, Error, PartialEq, Eq)]
67pub enum EventError {
68 #[error("event type cannot contain carriage returns")]
69 TypeHasCarriageReturn,
70
71 #[error("event type cannot contain newlines")]
72 TypeHasNewline,
73
74 #[error("event data cannot be empty")]
75 DataIsEmpty,
76
77 #[error("event data cannot contain carriage returns")]
78 DataHasCarriageReturn,
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 fn assert_to_writer(event: &Event, expected: &str) {
86 let mut bytes = vec![];
87 event.to_writer(&mut bytes).unwrap();
88 assert_eq!(std::str::from_utf8(&bytes).unwrap(), expected, "event: {:?}", event);
89 }
90
91 #[test]
92 fn error_if_type_has_carriage_return() {
93 assert_eq!(Event::from_type_and_data("\r", "data"), Err(EventError::TypeHasCarriageReturn));
94 }
95
96 #[test]
97 fn error_if_type_has_newline() {
98 assert_eq!(Event::from_type_and_data("\n", "data"), Err(EventError::TypeHasNewline));
99 }
100
101 #[test]
102 fn error_if_data_is_empty() {
103 assert_eq!(Event::from_type_and_data("", ""), Err(EventError::DataIsEmpty));
104 }
105
106 #[test]
107 fn error_if_data_has_carriage_return() {
108 assert_eq!(Event::from_type_and_data("", "\r"), Err(EventError::DataHasCarriageReturn));
109 }
110
111 #[test]
112 fn to_writer_type_and_data() {
113 let event = Event::from_type_and_data("type", "data").unwrap();
114 assert_to_writer(&event, "event: type\ndata: data\n\n");
115 }
116
117 #[test]
118 fn to_writer_no_type() {
119 let event = Event::from_type_and_data("", "data").unwrap();
120 assert_to_writer(&event, "data: data\n\n");
121 }
122
123 #[test]
124 fn to_writer_data_trailing_newline() {
125 let event = Event::from_type_and_data("", "data\n").unwrap();
126 assert_to_writer(&event, "data: data\ndata: \n\n");
127 }
128
129 #[test]
130 fn to_writer_two_line_data() {
131 let event = Event::from_type_and_data("", "data1\ndata2").unwrap();
132 assert_to_writer(&event, "data: data1\ndata: data2\n\n");
133 }
134
135 #[test]
136 fn to_writer_two_line_data_trailing_newline() {
137 let event = Event::from_type_and_data("", "data1\ndata2\n").unwrap();
138 assert_to_writer(&event, "data: data1\ndata: data2\ndata: \n\n");
139 }
140
141 #[test]
142 fn to_writer_consecutive_newlines() {
143 let event = Event::from_type_and_data("", "\n\n").unwrap();
144 assert_to_writer(&event, "data: \ndata: \ndata: \n\n");
145 }
146}