clientix_core/client/asynchronous/stream/
sse.rs1use crate::client::asynchronous::stream::ClientixStream;
2use crate::client::response::{ClientixError, ClientixResult};
3use futures_core::Stream;
4use futures_util::{StreamExt, TryStreamExt};
5use http::{HeaderMap, StatusCode, Version};
6use reqwest::Url;
7use std::net::SocketAddr;
8use std::pin::Pin;
9use std::str::FromStr;
10use std::task::{Context, Poll};
11use encoding_rs::UTF_8;
12use serde::de::DeserializeOwned;
13
14const ID_PROPERTY: &str = "id:";
15const EVENT_PROPERTY: &str = "event:";
16const COMMENT_PROPERTY: &str = ":";
17const RETRY_PROPERTY: &str = "retry:";
18const DATA_PROPERTY: &str = "data:";
19
20#[derive(Debug, Clone)]
21pub struct SSE<T> {
22 id: Option<String>,
23 event: Option<String>,
24 comment: Option<String>,
25 retry: Option<u64>,
26 data: Option<T>
27}
28
29impl<T> SSE<T> {
30
31 fn new() -> Self {
32 Self {
33 id: None,
34 event: None,
35 comment: None,
36 retry: None,
37 data: None,
38 }
39 }
40
41 pub fn id(&self) -> &Option<String> {
42 &self.id
43 }
44
45 pub fn event(&self) -> &Option<String> {
46 &self.event
47 }
48
49 pub fn comment(&self) -> &Option<String> {
50 &self.comment
51 }
52
53 pub fn retry(&self) -> &Option<u64> {
54 &self.retry
55 }
56
57 pub fn data(self) -> Option<T> {
58 self.data
59 }
60
61}
62
63pub struct ClientixSSEStream<T> {
64 version: Version,
65 content_length: Option<u64>,
66 status: StatusCode,
67 url: Url,
68 remote_addr: Option<SocketAddr>,
69 headers: HeaderMap,
70 stream: Pin<Box<dyn Stream<Item = ClientixResult<SSE<T>>>>>,
71}
72
73impl<T> ClientixSSEStream<T> {
74
75 pub fn new(
76 version: Version,
77 content_length: Option<u64>,
78 status: StatusCode,
79 url: Url,
80 remote_addr: Option<SocketAddr>,
81 headers: HeaderMap,
82 stream: impl Stream<Item = ClientixResult<SSE<T>>> + 'static
83 ) -> Self {
84 Self {
85 version,
86 content_length,
87 status,
88 url,
89 remote_addr,
90 headers,
91 stream: Box::pin(stream)
92 }
93 }
94
95 pub fn version(&self) -> Version {
96 self.version
97 }
98
99 pub fn content_length(&self) -> Option<u64> {
100 self.content_length
101 }
102
103 pub fn status(&self) -> StatusCode {
104 self.status
105 }
106
107 pub fn url(&self) -> &Url {
108 &self.url
109 }
110
111 pub fn remote_addr(&self) -> Option<SocketAddr> {
112 self.remote_addr
113 }
114
115 pub fn headers(&self) -> &HeaderMap {
116 &self.headers
117 }
118
119 pub async fn execute<F>(mut self, mut handle: F) where F: FnMut(ClientixResult<SSE<T>>) {
120 while let Some(result) = self.stream.next().await {
121 handle(result);
122 }
123 }
124
125 pub async fn collect(self) -> ClientixResult<Vec<SSE<T>>> {
126 self.stream.try_collect().await
127 }
128
129}
130
131impl ClientixSSEStream<String> {
132
133 pub fn object_stream<T, F>(self, mut convert: F) -> ClientixSSEStream<T>
134 where T: DeserializeOwned + Clone, F: FnMut(&str) -> ClientixResult<T> + 'static {
135 let version = self.version();
136 let content_length = self.content_length();
137 let status = self.status();
138 let url = self.url().clone();
139 let remote_addr = self.remote_addr();
140 let headers = self.headers().clone();
141 let stream = self
142 .filter(|line| match line {
143 Ok(line) if !line.data.clone().unwrap_or(String::new()).contains("[DONE]") => futures_util::future::ready(true),
144 _ => futures_util::future::ready(false)
145 })
146 .map(move |line| match line {
147 Ok(line) => {
148 let mut sse = SSE::new();
149 sse.id = line.id.clone();
150 sse.event = line.event.clone();
151 sse.comment = line.comment.clone();
152 sse.retry = line.retry;
153 sse.data = Some(convert(line.data.clone().unwrap_or(String::new()).as_str())?);
154
155 Ok(sse)
156 },
157 Err(err) => Err(err),
158 });
159
160 ClientixSSEStream::new(version, content_length, status, url, remote_addr, headers, stream)
161 }
162
163 pub fn json_stream<T>(self) -> ClientixSSEStream<T> where T: DeserializeOwned + Clone {
164 self.object_stream(|string| {
165 serde_json::from_str::<T>(string).map_err(ClientixError::from)
166 })
167 }
168
169 pub fn xml_stream<T>(self) -> ClientixSSEStream<T> where T: DeserializeOwned + Clone {
170 self.object_stream(|string| serde_xml_rs::from_str::<T>(string).map_err(ClientixError::from))
171 }
172
173}
174
175impl<T> Stream for ClientixSSEStream<T> {
176 type Item = ClientixResult<SSE<T>>;
177
178 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
179 self.stream.poll_next_unpin(cx)
180 }
181}
182
183impl From<ClientixStream> for ClientixSSEStream<String> {
184 fn from(stream: ClientixStream) -> Self {
185 let version = stream.version();
186 let content_length = stream.content_length();
187 let status = stream.status();
188 let url = stream.url().clone();
189 let remote_addr = stream.remote_addr();
190 let headers = stream.headers().clone();
191
192 let mut buffer = String::new();
193 let stream = stream
194 .map(|chunk| match chunk {
195 Ok(chunk) => {
196 let (text, _, _) = UTF_8.decode(&chunk);
197 Ok(text.to_string())
198 },
199 Err(error) => Err(error),
200 })
201 .flat_map(move |text| match text {
202 Ok(text) => {
203 let mut events = Vec::new();
204 for line in text.lines() {
205 let mut sse = SSE::new();
206 match line {
207 line if line.starts_with(ID_PROPERTY) => {
208 sse.id = line.strip_prefix(ID_PROPERTY)
209 .map(str::trim)
210 .map(str::to_string);
211 }
212 line if line.starts_with(EVENT_PROPERTY) => {
213 sse.event = line.strip_prefix(EVENT_PROPERTY)
214 .map(str::trim)
215 .map(str::to_string);
216 }
217 line if line.starts_with(COMMENT_PROPERTY) => {
218 sse.comment = line.strip_prefix(COMMENT_PROPERTY)
219 .map(str::trim)
220 .map(str::to_string);
221 }
222 line if line.starts_with(RETRY_PROPERTY) => {
223 sse.retry = line.strip_prefix(RETRY_PROPERTY)
224 .map(str::trim)
225 .map(u64::from_str)
226 .map(|result| match result {
227 Ok(value) => Some(value),
228 Err(_) => None
229 })
230 .unwrap_or(None);
231 }
232 line if line.starts_with(DATA_PROPERTY) => {
233 buffer.push_str(line.trim_start_matches(DATA_PROPERTY).trim());
234 }
235 _ => {
236 if !buffer.is_empty() {
237 sse.data = Some(buffer.to_string());
238 buffer.clear();
239
240 events.push(Ok(sse))
241 }
242 }
243 }
244 }
245
246 futures_util::stream::iter(events)
247 }
248 Err(error) => futures_util::stream::iter(vec![Err(error)])
249 });
250
251 ClientixSSEStream::new(
252 version,
253 content_length,
254 status,
255 url,
256 remote_addr,
257 headers,
258 stream
259 )
260 }
261}