titan/types/
sse.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
use futures_util::{Stream, StreamExt};
use titan_core::Respondable;
use titan_http::{
  body::Body,
  header::{HeaderMap, HeaderName, HeaderValue},
  Response,
};

#[derive(Clone)]
pub struct Sse<T>(pub T);

impl<T> Sse<T> {}

impl<T> Respondable for Sse<T>
where
  T: Stream<Item = Event> + Send + 'static,
{
  fn respond(self) -> titan_http::Response<Body> {
    let stream = self.0.map(|item| {
      let t: String = item.into();
      t.as_bytes().to_vec()
    });
    let mut response = Response::new(Body::Stream(Box::pin(stream)));
    *response.headers_mut() = HeaderMap::from_iter([
      (
        HeaderName::from_static("content-type"),
        HeaderValue::from_static("text/event-stream"),
      ),
      (
        HeaderName::from_static("cache-control"),
        HeaderValue::from_static("no-cache"),
      ),
      (
        HeaderName::from_static("connection"),
        HeaderValue::from_static("keep-alive"),
      ),
    ]);

    response
  }
}
#[derive(Clone)]
pub struct Event {
  data: Option<String>,
  event: Option<String>,
  id: Option<String>,
}

impl Event {
  pub fn new(data: String) -> Event {
    Event { data: Some(data), event: None, id: None }
  }
}

impl From<Event> for String {
  fn from(val: Event) -> Self {
    let mut text = String::new();

    if let Some(data) = val.data {
      text.push_str(&format!("data: {data}\n"));
    };

    if let Some(id) = val.id {
      text.push_str(&format!("id: {id}\n"));
    };

    if let Some(event) = val.event {
      text.push_str(&format!("event: {event}\n"));
    };

    text.push('\n');

    text
  }
}