rocket_community/data/
capped.rs

1/// Number of bytes read/written and whether that consisted of the entire
2/// stream.
3#[derive(Debug, Copy, Clone)]
4pub struct N {
5    /// The number of bytes written out.
6    pub written: u64,
7    /// Whether the entire stream was read and written out.
8    pub complete: bool,
9}
10
11impl std::fmt::Display for N {
12    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
13        self.written.fmt(f)
14    }
15}
16
17impl std::ops::Deref for N {
18    type Target = u64;
19
20    fn deref(&self) -> &Self::Target {
21        &self.written
22    }
23}
24
25/// Encapsulates a value capped to a data limit.
26///
27/// A `Capped<T>` type represents a `T` that has been limited (capped) to some
28/// number of bytes. The internal [`N`] specifies whether the value is complete
29/// (also [`Capped::is_complete()`]) or whether it was capped prematurely. A
30/// [`Capped`] is returned by various methods of [`DataStream`]. Some
31/// `Capped<T>` types, like `Capped<String>` and `Capped<TempFile>`, implement
32/// traits like [`FromData`] and [`FromForm`].
33///
34/// # Example
35///
36/// Since `Capped<TempFile>` implements `FromData`, it can be used as a data
37/// guard. The following Rocket route accepts a raw upload and stores the upload
38/// in a different directory depending on whether the file exceeded the data
39/// limit or not. See [`TempFile`] for details on temporary file storage
40/// locations and limits.
41///
42/// ```rust
43/// # #[macro_use] extern crate rocket_community as rocket;
44/// use rocket::data::Capped;
45/// use rocket::fs::TempFile;
46///
47/// #[post("/upload", data = "<file>")]
48/// async fn upload(mut file: Capped<TempFile<'_>>) -> std::io::Result<()> {
49///     if file.is_complete() {
50///         file.persist_to("/tmp/complete/file.txt").await?;
51///     } else {
52///         file.persist_to("/tmp/incomplete/file.txt").await?;
53///     }
54///
55///     Ok(())
56/// }
57/// ```
58///
59/// [`DataStream`]: crate::data::DataStream
60/// [`FromData`]: crate::data::FromData
61/// [`FromForm`]: crate::form::FromForm
62/// [`TempFile`]: crate::fs::TempFile
63// TODO: `Capped` not particularly usable outside Rocket due to coherence.
64#[derive(Debug, Copy, Clone)]
65pub struct Capped<T> {
66    /// The capped value itself.
67    pub value: T,
68    /// The number of bytes written and whether `value` is complete.
69    pub n: N,
70}
71
72impl<T> Capped<T> {
73    /// Creates a new `Capped` from a `value` and an `n`. Prefer to use
74    /// [`Capped::from()`] when possible.
75    ///
76    /// # Example
77    ///
78    /// ```rust
79    /// # extern crate rocket_community as rocket;
80    /// use rocket::data::{Capped, N};
81    ///
82    /// let n = N { written: 2, complete: true };
83    /// let capped = Capped::new("hi".to_string(), n);
84    /// ```
85    #[inline(always)]
86    pub fn new(value: T, n: N) -> Self {
87        Capped { value, n }
88    }
89
90    /// Creates a new `Capped` from a `value` and the length of `value` `n`,
91    /// marking `value` as complete. Prefer to use [`Capped::from()`] when
92    /// possible.
93    ///
94    /// # Example
95    ///
96    /// ```rust
97    /// # extern crate rocket_community as rocket;
98    /// use rocket::data::{Capped, N};
99    ///
100    /// let string = "hi";
101    /// let capped = Capped::complete("hi", string.len());
102    /// ```
103    #[inline(always)]
104    pub fn complete(value: T, len: usize) -> Self {
105        Capped {
106            value,
107            n: N {
108                written: len as u64,
109                complete: true,
110            },
111        }
112    }
113
114    /// Converts a `Capped<T>` to `Capped<U>` by applying `f` to the contained
115    /// value.
116    ///
117    /// # Example
118    ///
119    /// ```rust
120    /// # extern crate rocket_community as rocket;
121    /// use rocket::data::{Capped, N};
122    ///
123    /// let n = N { written: 2, complete: true };
124    /// let capped: Capped<usize> = Capped::new(10usize, n);
125    /// let mapped: Capped<String> = capped.map(|n| n.to_string());
126    /// ```
127    #[inline(always)]
128    pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Capped<U> {
129        Capped {
130            value: f(self.value),
131            n: self.n,
132        }
133    }
134
135    /// Returns `true` if `self.n.written` is `0`, that is, no bytes were
136    /// written to `value`.
137    ///
138    /// # Example
139    ///
140    /// ```rust
141    /// # extern crate rocket_community as rocket;
142    /// use rocket::data::{Capped, N};
143    ///
144    /// let n = N { written: 2, complete: true };
145    /// let capped = Capped::new("hi".to_string(), n);
146    /// assert!(!capped.is_empty());
147    ///
148    /// let n = N { written: 0, complete: true };
149    /// let capped = Capped::new("".to_string(), n);
150    /// assert!(capped.is_empty());
151    /// ```
152    #[inline(always)]
153    pub fn is_empty(&self) -> bool {
154        self.n.written == 0
155    }
156
157    /// Returns `true` if `self.n.complete`, that is, `value` represents the
158    /// entire data stream.
159    ///
160    /// # Example
161    ///
162    /// ```rust
163    /// # extern crate rocket_community as rocket;
164    /// use rocket::data::{Capped, N};
165    ///
166    /// let n = N { written: 2, complete: true };
167    /// let capped = Capped::new("hi".to_string(), n);
168    /// assert!(capped.is_complete());
169    ///
170    /// let n = N { written: 4, complete: false };
171    /// let capped = Capped::new("hell".to_string(), n);
172    /// assert!(!capped.is_complete());
173    /// ```
174    #[inline(always)]
175    pub fn is_complete(&self) -> bool {
176        self.n.complete
177    }
178
179    /// Returns the internal value.
180    ///
181    /// # Example
182    ///
183    /// ```rust
184    /// # extern crate rocket_community as rocket;
185    /// use rocket::data::{Capped, N};
186    ///
187    /// let n = N { written: 2, complete: true };
188    /// let capped = Capped::new("hi".to_string(), n);
189    /// assert_eq!(capped.into_inner(), "hi");
190    /// ```
191    #[inline(always)]
192    pub fn into_inner(self) -> T {
193        self.value
194    }
195}
196
197impl<T> std::ops::Deref for Capped<T> {
198    type Target = T;
199
200    fn deref(&self) -> &Self::Target {
201        &self.value
202    }
203}
204
205impl<T> std::ops::DerefMut for Capped<T> {
206    fn deref_mut(&mut self) -> &mut Self::Target {
207        &mut self.value
208    }
209}
210
211impl<T: AsRef<[u8]>> From<T> for Capped<T> {
212    /// Creates a `Capped<T>` from a `value`, setting `complete` to `true`.
213    fn from(value: T) -> Self {
214        let len = value.as_ref().len();
215        Capped::complete(value, len)
216    }
217}
218
219use crate::request::Request;
220use crate::response::{self, Responder};
221
222impl<'r, 'o: 'r, T: Responder<'r, 'o>> Responder<'r, 'o> for Capped<T> {
223    fn respond_to(self, request: &'r Request<'_>) -> response::Result<'o> {
224        self.value.respond_to(request)
225    }
226}
227
228macro_rules! impl_strict_from_form_field_from_capped {
229    ($T:ty) => {
230        const _: () = {
231            use $crate::data::Capped;
232            use $crate::form::{DataField, FromFormField, Result, ValueField};
233
234            #[crate::async_trait]
235            impl<'v> FromFormField<'v> for $T {
236                fn default() -> Option<Self> {
237                    <Capped<$T> as FromFormField<'v>>::default().map(|c| c.value)
238                }
239
240                fn from_value(f: ValueField<'v>) -> Result<'v, Self> {
241                    let capped = <Capped<$T> as FromFormField<'v>>::from_value(f)?;
242                    if !capped.is_complete() {
243                        Err((None, Some(capped.n.written)))?;
244                    }
245
246                    Ok(capped.value)
247                }
248
249                async fn from_data(field: DataField<'v, '_>) -> Result<'v, Self> {
250                    let capped = <Capped<$T> as FromFormField<'v>>::from_data(field);
251                    let capped = capped.await?;
252                    if !capped.is_complete() {
253                        Err((None, Some(capped.n.written)))?;
254                    }
255
256                    Ok(capped.value)
257                }
258            }
259        };
260    };
261}
262
263macro_rules! impl_strict_from_data_from_capped {
264    ($T:ty) => {
265        #[crate::async_trait]
266        impl<'r> $crate::data::FromData<'r> for $T {
267            type Error = <$crate::data::Capped<Self> as $crate::data::FromData<'r>>::Error;
268
269            async fn from_data(
270                r: &'r $crate::Request<'_>,
271                d: $crate::Data<'r>,
272            ) -> $crate::data::Outcome<'r, Self> {
273                use std::io::{Error, ErrorKind::UnexpectedEof};
274                use $crate::outcome::Outcome::*;
275
276                match <$crate::data::Capped<$T> as FromData>::from_data(r, d).await {
277                    Success(p) if p.is_complete() => Success(p.into_inner()),
278                    Success(_) => {
279                        let e = Error::new(UnexpectedEof, "data limit exceeded");
280                        Error((Status::BadRequest, e.into()))
281                    }
282                    Forward(d) => Forward(d),
283                    Error((s, e)) => Error((s, e)),
284                }
285            }
286        }
287    };
288}