rocket/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;
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    /// use rocket::data::{Capped, N};
80    ///
81    /// let n = N { written: 2, complete: true };
82    /// let capped = Capped::new("hi".to_string(), n);
83    /// ```
84    #[inline(always)]
85    pub fn new(value: T, n: N) -> Self {
86        Capped { value, n, }
87    }
88
89    /// Creates a new `Capped` from a `value` and the length of `value` `n`,
90    /// marking `value` as complete. Prefer to use [`Capped::from()`] when
91    /// possible.
92    ///
93    /// # Example
94    ///
95    /// ```rust
96    /// use rocket::data::{Capped, N};
97    ///
98    /// let string = "hi";
99    /// let capped = Capped::complete("hi", string.len());
100    /// ```
101    #[inline(always)]
102    pub fn complete(value: T, len: usize) -> Self {
103        Capped { value, n: N { written: len as u64, complete: true } }
104    }
105
106    /// Converts a `Capped<T>` to `Capped<U>` by applying `f` to the contained
107    /// value.
108    ///
109    /// # Example
110    ///
111    /// ```rust
112    /// use rocket::data::{Capped, N};
113    ///
114    /// let n = N { written: 2, complete: true };
115    /// let capped: Capped<usize> = Capped::new(10usize, n);
116    /// let mapped: Capped<String> = capped.map(|n| n.to_string());
117    /// ```
118    #[inline(always)]
119    pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Capped<U> {
120        Capped { value: f(self.value), n: self.n }
121    }
122
123    /// Returns `true` if `self.n.written` is `0`, that is, no bytes were
124    /// written to `value`.
125    ///
126    /// # Example
127    ///
128    /// ```rust
129    /// use rocket::data::{Capped, N};
130    ///
131    /// let n = N { written: 2, complete: true };
132    /// let capped = Capped::new("hi".to_string(), n);
133    /// assert!(!capped.is_empty());
134    ///
135    /// let n = N { written: 0, complete: true };
136    /// let capped = Capped::new("".to_string(), n);
137    /// assert!(capped.is_empty());
138    /// ```
139    #[inline(always)]
140    pub fn is_empty(&self) -> bool {
141        self.n.written == 0
142    }
143
144    /// Returns `true` if `self.n.complete`, that is, `value` represents the
145    /// entire data stream.
146    ///
147    /// # Example
148    ///
149    /// ```rust
150    /// use rocket::data::{Capped, N};
151    ///
152    /// let n = N { written: 2, complete: true };
153    /// let capped = Capped::new("hi".to_string(), n);
154    /// assert!(capped.is_complete());
155    ///
156    /// let n = N { written: 4, complete: false };
157    /// let capped = Capped::new("hell".to_string(), n);
158    /// assert!(!capped.is_complete());
159    /// ```
160    #[inline(always)]
161    pub fn is_complete(&self) -> bool {
162        self.n.complete
163    }
164
165    /// Returns the internal value.
166    ///
167    /// # Example
168    ///
169    /// ```rust
170    /// use rocket::data::{Capped, N};
171    ///
172    /// let n = N { written: 2, complete: true };
173    /// let capped = Capped::new("hi".to_string(), n);
174    /// assert_eq!(capped.into_inner(), "hi");
175    /// ```
176    #[inline(always)]
177    pub fn into_inner(self) -> T {
178        self.value
179    }
180}
181
182impl<T> std::ops::Deref for Capped<T> {
183    type Target = T;
184
185    fn deref(&self) -> &Self::Target {
186        &self.value
187    }
188}
189
190impl<T> std::ops::DerefMut for Capped<T> {
191    fn deref_mut(&mut self) -> &mut Self::Target {
192        &mut self.value
193    }
194}
195
196impl<T: AsRef<[u8]>> From<T> for Capped<T> {
197    /// Creates a `Capped<T>` from a `value`, setting `complete` to `true`.
198    fn from(value: T) -> Self {
199        let len = value.as_ref().len();
200        Capped::complete(value, len)
201    }
202}
203
204use crate::response::{self, Responder};
205use crate::request::Request;
206
207impl<'r, 'o: 'r, T: Responder<'r, 'o>> Responder<'r, 'o> for Capped<T> {
208    fn respond_to(self, request: &'r Request<'_>) -> response::Result<'o> {
209        self.value.respond_to(request)
210    }
211}
212
213macro_rules! impl_strict_from_form_field_from_capped {
214    ($T:ty) => (const _: () = {
215        use $crate::form::{FromFormField, ValueField, DataField, Result};
216        use $crate::data::Capped;
217
218        #[crate::async_trait]
219        impl<'v> FromFormField<'v> for $T {
220            fn default() -> Option<Self> {
221                <Capped<$T> as FromFormField<'v>>::default().map(|c| c.value)
222            }
223
224            fn from_value(f: ValueField<'v>) -> Result<'v, Self> {
225                let capped = <Capped<$T> as FromFormField<'v>>::from_value(f)?;
226                if !capped.is_complete() {
227                    Err((None, Some(capped.n.written)))?;
228                }
229
230                Ok(capped.value)
231            }
232
233            async fn from_data(field: DataField<'v, '_>) -> Result<'v, Self> {
234                let capped = <Capped<$T> as FromFormField<'v>>::from_data(field);
235                let capped = capped.await?;
236                if !capped.is_complete() {
237                    Err((None, Some(capped.n.written)))?;
238                }
239
240                Ok(capped.value)
241            }
242        }
243    };)
244}
245
246macro_rules! impl_strict_from_data_from_capped {
247    ($T:ty) => (
248        #[crate::async_trait]
249        impl<'r> $crate::data::FromData<'r> for $T {
250            type Error = <$crate::data::Capped<Self> as $crate::data::FromData<'r>>::Error;
251
252            async fn from_data(
253                r: &'r $crate::Request<'_>,
254                d: $crate::Data<'r>
255            ) -> $crate::data::Outcome<'r, Self> {
256                use $crate::outcome::Outcome::*;
257                use std::io::{Error, ErrorKind::UnexpectedEof};
258
259                match <$crate::data::Capped<$T> as FromData>::from_data(r, d).await {
260                    Success(p) if p.is_complete() => Success(p.into_inner()),
261                    Success(_) => {
262                        let e = Error::new(UnexpectedEof, "data limit exceeded");
263                        Error((Status::BadRequest, e.into()))
264                    },
265                    Forward(d) => Forward(d),
266                    Error((s, e)) => Error((s, e)),
267                }
268            }
269        }
270    )
271}