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}