server_sent_events/
event.rs

1use alloc::{boxed::Box, vec::Vec};
2
3#[derive(Debug, Clone, Default, PartialEq, Eq)]
4pub struct Event {
5    //
6    pub comment: Option<Box<str>>,
7    //
8    pub retry: Option<usize>,
9    //
10    pub id: Option<Box<str>>,
11    pub r#type: Option<Box<str>>,
12    pub data: Option<EventData>,
13}
14
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum EventData {
17    Line(Box<str>),
18    Lines(Vec<Box<str>>),
19}
20
21impl Event {
22    pub fn new<'a>(
23        data: &str,
24        r#type: impl Into<Option<&'a str>>,
25        id: impl Into<Option<&'a str>>,
26    ) -> Self {
27        Self {
28            id: id.into().map(Into::into),
29            r#type: r#type.into().map(Into::into),
30            data: Some(EventData::Line(data.into())),
31            ..Default::default()
32        }
33    }
34
35    pub fn with_multiline_data<'a>(
36        data: Vec<&str>,
37        r#type: impl Into<Option<&'a str>>,
38        id: impl Into<Option<&'a str>>,
39    ) -> Self {
40        Self {
41            id: id.into().map(Into::into),
42            r#type: r#type.into().map(Into::into),
43            data: Some(EventData::Lines(data.into_iter().map(Into::into).collect())),
44            ..Default::default()
45        }
46    }
47
48    pub fn with_comment(comment: &str) -> Self {
49        Self {
50            comment: Some(comment.into()),
51            ..Default::default()
52        }
53    }
54
55    pub fn with_retry(retry_ms: usize) -> Self {
56        Self {
57            retry: Some(retry_ms),
58            ..Default::default()
59        }
60    }
61}
62
63impl core::fmt::Display for Event {
64    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
65        if let Some(comment) = &self.comment {
66            writeln!(f, ": {comment}")?;
67        }
68
69        if let Some(retry) = &self.retry {
70            writeln!(f, "retry: {retry}")?;
71        }
72
73        if let Some(id) = &self.id {
74            writeln!(f, "id: {id}")?;
75        }
76
77        if let Some(r#type) = &self.r#type {
78            writeln!(f, "event: {type}")?;
79        }
80
81        if let Some(data) = &self.data {
82            match &data {
83                EventData::Line(s) => {
84                    for x in s.lines() {
85                        writeln!(f, "data: {x}")?;
86                    }
87                }
88                &EventData::Lines(s_vec) => {
89                    for s in s_vec {
90                        for x in s.lines() {
91                            writeln!(f, "data: {x}")?;
92                        }
93                    }
94                }
95            }
96        }
97
98        writeln!(f)?;
99
100        Ok(())
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107
108    use alloc::{string::ToString as _, vec};
109
110    #[test]
111    fn test_to_string() {
112        assert_eq!(
113            Event::with_comment("This is a comment")
114                .to_string()
115                .as_bytes(),
116            b": This is a comment\n\n"
117        );
118
119        assert_eq!(
120            Event::with_retry(10000).to_string().as_bytes(),
121            b"retry: 10000\n\n"
122        );
123
124        assert_eq!(
125            Event::new("message", None, None).to_string().as_bytes(),
126            b"data: message\n\n"
127        );
128        assert_eq!(
129            Event::new(r#"Message of type "foo""#, "foo", None)
130                .to_string()
131                .as_bytes(),
132            b"event: foo\ndata: Message of type \"foo\"\n\n"
133        );
134        assert_eq!(
135            Event::new(r#"Message of type "foo""#, "foo", "1")
136                .to_string()
137                .as_bytes(),
138            b"id: 1\nevent: foo\ndata: Message of type \"foo\"\n\n"
139        );
140
141        assert_eq!(
142            Event::with_multiline_data(
143                vec!["Multi-line message of", r#"type "bar" and id "42""#],
144                "bar",
145                "42"
146            )
147            .to_string()
148            .as_bytes(),
149            b"id: 42\nevent: bar\ndata: Multi-line message of\ndata: type \"bar\" and id \"42\"\n\n"
150        );
151
152        assert_eq!(
153            Event::with_multiline_data(vec!["another message", "with two lines "], None, None)
154                .to_string()
155                .as_bytes(),
156            b"data: another message\ndata: with two lines \n\n"
157        );
158    }
159}