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}