rocket/response/stream/text.rs
1use futures::stream::{Stream, StreamExt};
2
3use crate::request::Request;
4use crate::response::{self, Response, Responder};
5use crate::http::ContentType;
6use crate::response::stream::ReaderStream;
7
8/// A potentially infinite stream of text: `T: AsRef<str>`.
9///
10/// A `TextStream` can be constructed from any [`Stream`] of items of type `T`
11/// where `T: AsRef<str>`. This includes `&str`, `String`, `Cow<str>`,
12/// `&RawStr`, and more. The stream can be constructed directly, via
13/// `TextStream(..)` or [`TextStream::from()`], or through generator syntax via
14/// [`TextStream!`].
15///
16/// [`Stream`]: https://docs.rs/futures/0.3/futures/stream/trait.Stream.html
17///
18/// # Responder
19///
20/// `TextStream` is a (potentially infinite) responder. The response
21/// `Content-Type` is set to [`Text`](ContentType::Text). The body is
22/// [unsized](crate::response::Body#unsized), and values are sent as soon as
23/// they are yielded by the internal iterator.
24///
25/// # Example
26///
27/// Use [`TextStream!`] to yield 10 strings, one every second, of the form `"n:
28/// $k"` for `$k` from `0` to `10` exclusive:
29///
30/// ```rust
31/// # use rocket::*;
32/// use rocket::response::stream::TextStream;
33/// use rocket::futures::stream::{repeat, StreamExt};
34/// use rocket::tokio::time::{self, Duration};
35///
36/// #[get("/text")]
37/// fn text() -> TextStream![&'static str] {
38/// TextStream(repeat("hi"))
39/// }
40///
41/// #[get("/text/stream")]
42/// fn stream() -> TextStream![String] {
43/// TextStream! {
44/// let mut interval = time::interval(Duration::from_secs(1));
45/// for i in 0..10 {
46/// yield format!("n: {}", i);
47/// interval.tick().await;
48/// }
49/// }
50/// }
51/// ```
52///
53/// The syntax of [`TextStream!`] as an expression is identical to that of
54/// [`stream!`](crate::response::stream::stream).
55#[derive(Debug, Clone)]
56pub struct TextStream<S>(pub S);
57
58impl<S> From<S> for TextStream<S> {
59 /// Creates a `TextStream` from any `S: Stream`.
60 fn from(stream: S) -> Self {
61 TextStream(stream)
62 }
63}
64
65impl<'r, S: Stream> Responder<'r, 'r> for TextStream<S>
66 where S: Send + 'r, S::Item: AsRef<str> + Send + Unpin + 'r
67{
68 fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> {
69 struct ByteStr<T>(T);
70
71 impl<T: AsRef<str>> AsRef<[u8]> for ByteStr<T> {
72 fn as_ref(&self) -> &[u8] {
73 self.0.as_ref().as_bytes()
74 }
75 }
76
77 let inner = self.0.map(ByteStr).map(std::io::Cursor::new);
78 Response::build()
79 .header(ContentType::Text)
80 .streamed_body(ReaderStream::from(inner))
81 .ok()
82 }
83}
84
85crate::export! {
86 /// Type and stream expression macro for [`struct@TextStream`].
87 ///
88 /// See [`stream!`](crate::response::stream::stream) for the syntax
89 /// supported by this macro.
90 ///
91 /// See [`struct@TextStream`] and the [module level
92 /// docs](crate::response::stream#typed-streams) for usage details.
93 macro_rules! TextStream {
94 ($($s:tt)*) => ($crate::_typed_stream!(TextStream, $($s)*));
95 }
96}