rocket/request/from_param.rs
1use std::str::FromStr;
2use std::path::PathBuf;
3
4use crate::error::Empty;
5use crate::either::Either;
6use crate::http::uri::{Segments, error::PathError, fmt::Path};
7
8/// Trait to convert a dynamic path segment string to a concrete value.
9///
10/// This trait is used by Rocket's code generation facilities to parse dynamic
11/// path segment string values into a given type. That is, when a path contains
12/// a dynamic segment `<param>` where `param` has some type `T` that implements
13/// `FromParam`, `T::from_param` will be called.
14///
15/// # Deriving
16///
17/// The `FromParam` trait can be automatically derived for C-like enums. See
18/// [`FromParam` derive](macro@rocket::FromParam) for more information.
19///
20/// # Forwarding
21///
22/// If the conversion fails, the incoming request will be forwarded to the next
23/// matching route, if any. For instance, consider the following route and
24/// handler for the dynamic `"/<id>"` path:
25///
26/// ```rust
27/// # #[macro_use] extern crate rocket;
28/// #[get("/<id>")]
29/// fn hello(id: usize) -> String {
30/// # let _id = id;
31/// # /*
32/// ...
33/// # */
34/// # "".to_string()
35/// }
36/// # fn main() { }
37/// ```
38///
39/// If `usize::from_param` returns an `Ok(usize)` variant, the encapsulated
40/// value is used as the `id` function parameter. If not, the request is
41/// forwarded to the next matching route. Since there are no additional matching
42/// routes, this example will result in a 422 error for requests with invalid
43/// `id` values.
44///
45/// # Catching Errors
46///
47/// Sometimes, a forward is not desired, and instead, we simply want to know
48/// that the dynamic path segment could not be parsed into some desired type
49/// `T`. In these cases, types of `Option<T>`, `Result<T, T::Error>`, or
50/// `Either<A, B>` can be used, which implement `FromParam` themselves.
51///
52/// * **`Option<T>`** _where_ **`T: FromParam`**
53///
54/// Always returns successfully.
55///
56/// If the conversion to `T` fails, `None` is returned. If the conversion
57/// succeeds, `Some(value)` is returned.
58///
59/// * **`Result<T, T::Error>`** _where_ **`T: FromParam`**
60///
61/// Always returns successfully.
62///
63/// If the conversion to `T` fails, `Err(error)` is returned. If the
64/// conversion succeeds, `Ok(value)` is returned.
65///
66/// * **`Either<A, B>`** _where_ **`A: FromParam`** _and_ **`B: FromParam`**
67///
68/// Fails only when both `A::from_param` and `B::from_param` fail. If one
69/// of the two succeeds, the successful value is returned in
70/// `Either::Left(A)` or `Either::Right(B)` variant, respectively. If both
71/// fail, the error values from both calls are returned in a tuple in the
72/// `Err` variant.
73///
74/// `Either<A, B>` is particularly useful with a `B` type of `&str`, allowing
75/// you to retrieve the invalid path segment. Because `&str`'s implementation of
76/// `FromParam` always succeeds, the `Right` variant of the `Either` will always
77/// contain the path segment in case of failure.
78///
79/// For instance, consider the following route and handler:
80///
81/// ```rust
82/// # #[macro_use] extern crate rocket;
83/// use rocket::either::{Either, Left, Right};
84///
85/// #[get("/<id>")]
86/// fn hello(id: Either<usize, &str>) -> String {
87/// match id {
88/// Left(id_num) => format!("usize: {}", id_num),
89/// Right(string) => format!("Not a usize: {}", string)
90/// }
91/// }
92/// # fn main() { }
93/// ```
94///
95/// In the above example, if the dynamic path segment cannot be parsed into a
96/// `usize`, the raw path segment is returned in the `Right` variant of the
97/// `Either<usize, &str>` value.
98///
99/// # Provided Implementations
100///
101/// Rocket implements `FromParam` for several standard library types. Their
102/// behavior is documented here.
103///
104/// *
105/// * Primitive types: **f32, f64, isize, i8, i16, i32, i64, i128,
106/// usize, u8, u16, u32, u64, u128, bool**
107/// * `IpAddr` and `SocketAddr` types: **IpAddr, Ipv4Addr, Ipv6Addr,
108/// SocketAddrV4, SocketAddrV6, SocketAddr**
109/// * `NonZero*` types: **NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64,
110/// NonZeroI128, NonZeroIsize, NonZeroU8, NonZeroU16, NonZeroU32,
111/// NonZeroU64, NonZeroU128, NonZeroUsize**
112///
113/// A value is parsed successfully if the `from_str` method from the given
114/// type returns successfully. Otherwise, the raw path segment is returned
115/// in the `Err` value.
116///
117/// * **&str, String**
118///
119/// _This implementation always returns successfully._
120///
121/// Returns the percent-decoded path segment with invalid UTF-8 byte
122/// sequences replaced by � U+FFFD.
123///
124/// * **Option<T>** _where_ **T: FromParam**
125///
126/// _This implementation always returns successfully._
127///
128/// The path segment is parsed by `T`'s `FromParam` implementation. If the
129/// parse succeeds, a `Some(parsed_value)` is returned. Otherwise, a `None`
130/// is returned.
131///
132/// * **Result<T, T::Error>** _where_ **T: FromParam**
133///
134/// _This implementation always returns successfully._
135///
136/// The path segment is parsed by `T`'s `FromParam` implementation. The
137/// returned `Result` value is returned.
138///
139/// # Example
140///
141/// Say you want to parse a segment of the form:
142///
143/// ```text
144/// [a-zA-Z]+:[0-9]+
145/// ```
146///
147/// into the following structure, where the string before the `:` is stored in
148/// `key` and the number after the colon is stored in `value`:
149///
150/// ```rust
151/// struct MyParam<'r> {
152/// key: &'r str,
153/// value: usize
154/// }
155/// ```
156///
157/// The following implementation accomplishes this:
158///
159/// ```rust
160/// use rocket::request::FromParam;
161/// # #[allow(dead_code)]
162/// # struct MyParam<'r> { key: &'r str, value: usize }
163///
164/// impl<'r> FromParam<'r> for MyParam<'r> {
165/// type Error = &'r str;
166///
167/// fn from_param(param: &'r str) -> Result<Self, Self::Error> {
168/// // We can convert `param` into a `str` since we'll check every
169/// // character for safety later.
170/// let (key, val_str) = match param.find(':') {
171/// Some(i) if i > 0 => (¶m[..i], ¶m[(i + 1)..]),
172/// _ => return Err(param)
173/// };
174///
175/// if !key.chars().all(|c| c.is_ascii_alphabetic()) {
176/// return Err(param);
177/// }
178///
179/// val_str.parse()
180/// .map(|value| MyParam { key, value })
181/// .map_err(|_| param)
182/// }
183/// }
184/// ```
185///
186/// With the implementation, the `MyParam` type can be used as the target of a
187/// dynamic path segment:
188///
189/// ```rust
190/// # #[macro_use] extern crate rocket;
191/// # use rocket::request::FromParam;
192/// # #[allow(dead_code)]
193/// # struct MyParam<'r> { key: &'r str, value: usize }
194/// # impl<'r> FromParam<'r> for MyParam<'r> {
195/// # type Error = &'r str;
196/// # fn from_param(param: &'r str) -> Result<Self, Self::Error> {
197/// # Err(param)
198/// # }
199/// # }
200/// #
201/// #[get("/<key_val>")]
202/// fn hello(key_val: MyParam) -> String {
203/// # let _kv = key_val;
204/// # /*
205/// ...
206/// # */
207/// # "".to_string()
208/// }
209/// # fn main() { }
210/// ```
211pub trait FromParam<'a>: Sized {
212 /// The associated error to be returned if parsing/validation fails.
213 type Error: std::fmt::Debug;
214
215 /// Parses and validates an instance of `Self` from a path parameter string
216 /// or returns an `Error` if parsing or validation fails.
217 fn from_param(param: &'a str) -> Result<Self, Self::Error>;
218}
219
220impl<'a> FromParam<'a> for &'a str {
221 type Error = Empty;
222
223 #[inline(always)]
224 fn from_param(param: &'a str) -> Result<&'a str, Self::Error> {
225 if param.is_empty() {
226 return Err(Empty);
227 }
228
229 Ok(param)
230 }
231}
232
233impl<'a> FromParam<'a> for String {
234 type Error = Empty;
235
236 #[track_caller]
237 #[inline(always)]
238 fn from_param(param: &'a str) -> Result<String, Self::Error> {
239 #[cfg(debug_assertions)] {
240 let location = std::panic::Location::caller();
241 warn!(%location, "`String` as a parameter is inefficient. Use `&str` instead.");
242 }
243
244 if param.is_empty() {
245 return Err(Empty);
246 }
247
248 Ok(param.to_string())
249 }
250}
251
252macro_rules! impl_with_fromstr {
253 ($($T:ty),+) => ($(
254 impl<'a> FromParam<'a> for $T {
255 type Error = <$T as FromStr>::Err;
256
257 #[inline(always)]
258 fn from_param(param: &'a str) -> Result<Self, Self::Error> {
259 <$T as FromStr>::from_str(param)
260 }
261 }
262 )+)
263}
264
265use std::num::{
266 NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize,
267 NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize,
268};
269use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr};
270
271impl_with_fromstr! {
272 i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64,
273 NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize,
274 NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize,
275 bool, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr
276}
277
278impl<'a> FromParam<'a> for PathBuf {
279 type Error = PathError;
280
281 #[inline]
282 fn from_param(param: &'a str) -> Result<Self, Self::Error> {
283 use crate::http::private::Indexed;
284
285 let segments = &[Indexed::Indexed(0, param.len())];
286 Segments::new(param.into(), segments).to_path_buf(false)
287 }
288}
289
290impl<'a, T: FromParam<'a>> FromParam<'a> for Result<T, T::Error> {
291 type Error = std::convert::Infallible;
292
293 #[inline]
294 fn from_param(param: &'a str) -> Result<Self, Self::Error> {
295 match T::from_param(param) {
296 Ok(val) => Ok(Ok(val)),
297 Err(e) => Ok(Err(e)),
298 }
299 }
300}
301
302impl<'a, T: FromParam<'a>> FromParam<'a> for Option<T> {
303 type Error = std::convert::Infallible;
304
305 #[inline]
306 fn from_param(param: &'a str) -> Result<Self, Self::Error> {
307 match T::from_param(param) {
308 Ok(val) => Ok(Some(val)),
309 Err(_) => Ok(None)
310 }
311 }
312}
313
314/// Trait to convert _many_ dynamic path segment strings to a concrete value.
315///
316/// This is the `..` analog to [`FromParam`], and its functionality is identical
317/// to it with one exception: this trait applies to segment parameters of the
318/// form `<param..>`, where `param` is of some type `T` that implements
319/// `FromSegments`. `T::from_segments` is called to convert the matched segments
320/// (via the [`Segments`] iterator) into the implementing type.
321///
322/// # Provided Implementations
323///
324/// **`PathBuf`**
325///
326/// The `PathBuf` implementation constructs a path from the segments iterator.
327/// Each segment is percent-decoded. If a segment equals ".." before or after
328/// decoding, the previous segment (if any) is omitted. For security purposes,
329/// any other segments that begin with "*" or "." are ignored. If a
330/// percent-decoded segment results in invalid UTF8, an `Err` is returned with
331/// the `Utf8Error`.
332pub trait FromSegments<'r>: Sized {
333 /// The associated error to be returned when parsing fails.
334 type Error: std::fmt::Debug;
335
336 /// Parses an instance of `Self` from many dynamic path parameter strings or
337 /// returns an `Error` if one cannot be parsed.
338 fn from_segments(segments: Segments<'r, Path>) -> Result<Self, Self::Error>;
339}
340
341impl<'r> FromSegments<'r> for Segments<'r, Path> {
342 type Error = std::convert::Infallible;
343
344 #[inline(always)]
345 fn from_segments(segments: Self) -> Result<Self, Self::Error> {
346 Ok(segments)
347 }
348}
349
350/// Creates a `PathBuf` from a `Segments` iterator. The returned `PathBuf` is
351/// percent-decoded. If a segment is equal to "..", the previous segment (if
352/// any) is skipped.
353///
354/// For security purposes, if a segment meets any of the following conditions,
355/// an `Err` is returned indicating the condition met:
356///
357/// * Decoded segment starts with any of: `.` (except `..`), `*`
358/// * Decoded segment ends with any of: `:`, `>`, `<`
359/// * Decoded segment contains any of: `/`
360/// * On Windows, decoded segment contains any of: `\`
361/// * Percent-encoding results in invalid UTF8.
362///
363/// As a result of these conditions, a `PathBuf` derived via `FromSegments` is
364/// safe to interpolate within, or use as a suffix of, a path without additional
365/// checks.
366impl FromSegments<'_> for PathBuf {
367 type Error = PathError;
368
369 fn from_segments(segments: Segments<'_, Path>) -> Result<Self, Self::Error> {
370 segments.to_path_buf(false)
371 }
372}
373
374impl<'r, T: FromSegments<'r>> FromSegments<'r> for Result<T, T::Error> {
375 type Error = std::convert::Infallible;
376
377 #[inline]
378 fn from_segments(segments: Segments<'r, Path>) -> Result<Result<T, T::Error>, Self::Error> {
379 match T::from_segments(segments) {
380 Ok(val) => Ok(Ok(val)),
381 Err(e) => Ok(Err(e)),
382 }
383 }
384}
385
386impl<'r, T: FromSegments<'r>> FromSegments<'r> for Option<T> {
387 type Error = std::convert::Infallible;
388
389 #[inline]
390 fn from_segments(segments: Segments<'r, Path>) -> Result<Option<T>, Self::Error> {
391 match T::from_segments(segments) {
392 Ok(val) => Ok(Some(val)),
393 Err(_) => Ok(None)
394 }
395 }
396}
397
398/// Implements `FromParam` for `Either<A, B>`, where `A` and `B` both implement
399/// `FromParam`. If `A::from_param` returns `Ok(a)`, `Either::Left(a)` is
400/// returned. If `B::from_param` returns `Ok(b)`, `Either::Right(b)` is
401/// returned. If both `A::from_param` and `B::from_param` return `Err(a)` and
402/// `Err(b)`, respectively, then `Err((a, b))` is returned.
403impl<'v, A: FromParam<'v>, B: FromParam<'v>> FromParam<'v> for Either<A, B> {
404 type Error = (A::Error, B::Error);
405
406 #[inline(always)]
407 fn from_param(param: &'v str) -> Result<Self, Self::Error> {
408 match A::from_param(param) {
409 Ok(a) => Ok(Either::Left(a)),
410 Err(a) => match B::from_param(param) {
411 Ok(b) => Ok(Either::Right(b)),
412 Err(b) => Err((a, b)),
413 }
414 }
415 }
416}