rocket_community/request/from_param.rs
1use std::path::PathBuf;
2use std::str::FromStr;
3
4use crate::either::Either;
5use crate::error::Empty;
6use crate::http::uri::{error::PathError, fmt::Path, Segments};
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_community as 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_community as 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/// # extern crate rocket_community as rocket;
161/// use rocket::request::FromParam;
162/// # #[allow(dead_code)]
163/// # struct MyParam<'r> { key: &'r str, value: usize }
164///
165/// impl<'r> FromParam<'r> for MyParam<'r> {
166/// type Error = &'r str;
167///
168/// fn from_param(param: &'r str) -> Result<Self, Self::Error> {
169/// // We can convert `param` into a `str` since we'll check every
170/// // character for safety later.
171/// let (key, val_str) = match param.find(':') {
172/// Some(i) if i > 0 => (¶m[..i], ¶m[(i + 1)..]),
173/// _ => return Err(param)
174/// };
175///
176/// if !key.chars().all(|c| c.is_ascii_alphabetic()) {
177/// return Err(param);
178/// }
179///
180/// val_str.parse()
181/// .map(|value| MyParam { key, value })
182/// .map_err(|_| param)
183/// }
184/// }
185/// ```
186///
187/// With the implementation, the `MyParam` type can be used as the target of a
188/// dynamic path segment:
189///
190/// ```rust
191/// # #[macro_use] extern crate rocket_community as rocket;
192/// # use rocket::request::FromParam;
193/// # #[allow(dead_code)]
194/// # struct MyParam<'r> { key: &'r str, value: usize }
195/// # impl<'r> FromParam<'r> for MyParam<'r> {
196/// # type Error = &'r str;
197/// # fn from_param(param: &'r str) -> Result<Self, Self::Error> {
198/// # Err(param)
199/// # }
200/// # }
201/// #
202/// #[get("/<key_val>")]
203/// fn hello(key_val: MyParam) -> String {
204/// # let _kv = key_val;
205/// # /*
206/// ...
207/// # */
208/// # "".to_string()
209/// }
210/// # fn main() { }
211/// ```
212pub trait FromParam<'a>: Sized {
213 /// The associated error to be returned if parsing/validation fails.
214 type Error: std::fmt::Debug;
215
216 /// Parses and validates an instance of `Self` from a path parameter string
217 /// or returns an `Error` if parsing or validation fails.
218 fn from_param(param: &'a str) -> Result<Self, Self::Error>;
219}
220
221impl<'a> FromParam<'a> for &'a str {
222 type Error = Empty;
223
224 #[inline(always)]
225 fn from_param(param: &'a str) -> Result<&'a str, Self::Error> {
226 if param.is_empty() {
227 return Err(Empty);
228 }
229
230 Ok(param)
231 }
232}
233
234impl<'a> FromParam<'a> for String {
235 type Error = Empty;
236
237 #[track_caller]
238 #[inline(always)]
239 fn from_param(param: &'a str) -> Result<String, Self::Error> {
240 #[cfg(debug_assertions)]
241 {
242 let location = std::panic::Location::caller();
243 warn!(%location, "`String` as a parameter is inefficient. Use `&str` instead.");
244 }
245
246 if param.is_empty() {
247 return Err(Empty);
248 }
249
250 Ok(param.to_string())
251 }
252}
253
254macro_rules! impl_with_fromstr {
255 ($($T:ty),+) => ($(
256 impl<'a> FromParam<'a> for $T {
257 type Error = <$T as FromStr>::Err;
258
259 #[inline(always)]
260 fn from_param(param: &'a str) -> Result<Self, Self::Error> {
261 <$T as FromStr>::from_str(param)
262 }
263 }
264 )+)
265}
266
267use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
268use std::num::{
269 NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
270 NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize,
271};
272
273impl_with_fromstr! {
274 i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64,
275 NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize,
276 NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize,
277 bool, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr
278}
279
280impl<'a> FromParam<'a> for PathBuf {
281 type Error = PathError;
282
283 #[inline]
284 fn from_param(param: &'a str) -> Result<Self, Self::Error> {
285 use crate::http::private::Indexed;
286
287 let segments = &[Indexed::Indexed(0, param.len())];
288 Segments::new(param.into(), segments).to_path_buf(false)
289 }
290}
291
292impl<'a, T: FromParam<'a>> FromParam<'a> for Result<T, T::Error> {
293 type Error = std::convert::Infallible;
294
295 #[inline]
296 fn from_param(param: &'a str) -> Result<Self, Self::Error> {
297 match T::from_param(param) {
298 Ok(val) => Ok(Ok(val)),
299 Err(e) => Ok(Err(e)),
300 }
301 }
302}
303
304impl<'a, T: FromParam<'a>> FromParam<'a> for Option<T> {
305 type Error = std::convert::Infallible;
306
307 #[inline]
308 fn from_param(param: &'a str) -> Result<Self, Self::Error> {
309 match T::from_param(param) {
310 Ok(val) => Ok(Some(val)),
311 Err(_) => Ok(None),
312 }
313 }
314}
315
316/// Trait to convert _many_ dynamic path segment strings to a concrete value.
317///
318/// This is the `..` analog to [`FromParam`], and its functionality is identical
319/// to it with one exception: this trait applies to segment parameters of the
320/// form `<param..>`, where `param` is of some type `T` that implements
321/// `FromSegments`. `T::from_segments` is called to convert the matched segments
322/// (via the [`Segments`] iterator) into the implementing type.
323///
324/// # Provided Implementations
325///
326/// **`PathBuf`**
327///
328/// The `PathBuf` implementation constructs a path from the segments iterator.
329/// Each segment is percent-decoded. If a segment equals ".." before or after
330/// decoding, the previous segment (if any) is omitted. For security purposes,
331/// any other segments that begin with "*" or "." are ignored. If a
332/// percent-decoded segment results in invalid UTF8, an `Err` is returned with
333/// the `Utf8Error`.
334pub trait FromSegments<'r>: Sized {
335 /// The associated error to be returned when parsing fails.
336 type Error: std::fmt::Debug;
337
338 /// Parses an instance of `Self` from many dynamic path parameter strings or
339 /// returns an `Error` if one cannot be parsed.
340 fn from_segments(segments: Segments<'r, Path>) -> Result<Self, Self::Error>;
341}
342
343impl<'r> FromSegments<'r> for Segments<'r, Path> {
344 type Error = std::convert::Infallible;
345
346 #[inline(always)]
347 fn from_segments(segments: Self) -> Result<Self, Self::Error> {
348 Ok(segments)
349 }
350}
351
352/// Creates a `PathBuf` from a `Segments` iterator. The returned `PathBuf` is
353/// percent-decoded. If a segment is equal to "..", the previous segment (if
354/// any) is skipped.
355///
356/// For security purposes, if a segment meets any of the following conditions,
357/// an `Err` is returned indicating the condition met:
358///
359/// * Decoded segment starts with any of: `.` (except `..`), `*`
360/// * Decoded segment ends with any of: `:`, `>`, `<`
361/// * Decoded segment contains any of: `/`
362/// * On Windows, decoded segment contains any of: `\`
363/// * Percent-encoding results in invalid UTF8.
364///
365/// As a result of these conditions, a `PathBuf` derived via `FromSegments` is
366/// safe to interpolate within, or use as a suffix of, a path without additional
367/// checks.
368impl FromSegments<'_> for PathBuf {
369 type Error = PathError;
370
371 fn from_segments(segments: Segments<'_, Path>) -> Result<Self, Self::Error> {
372 segments.to_path_buf(false)
373 }
374}
375
376impl<'r, T: FromSegments<'r>> FromSegments<'r> for Result<T, T::Error> {
377 type Error = std::convert::Infallible;
378
379 #[inline]
380 fn from_segments(segments: Segments<'r, Path>) -> Result<Result<T, T::Error>, Self::Error> {
381 match T::from_segments(segments) {
382 Ok(val) => Ok(Ok(val)),
383 Err(e) => Ok(Err(e)),
384 }
385 }
386}
387
388impl<'r, T: FromSegments<'r>> FromSegments<'r> for Option<T> {
389 type Error = std::convert::Infallible;
390
391 #[inline]
392 fn from_segments(segments: Segments<'r, Path>) -> Result<Option<T>, Self::Error> {
393 match T::from_segments(segments) {
394 Ok(val) => Ok(Some(val)),
395 Err(_) => Ok(None),
396 }
397 }
398}
399
400/// Implements `FromParam` for `Either<A, B>`, where `A` and `B` both implement
401/// `FromParam`. If `A::from_param` returns `Ok(a)`, `Either::Left(a)` is
402/// returned. If `B::from_param` returns `Ok(b)`, `Either::Right(b)` is
403/// returned. If both `A::from_param` and `B::from_param` return `Err(a)` and
404/// `Err(b)`, respectively, then `Err((a, b))` is returned.
405impl<'v, A: FromParam<'v>, B: FromParam<'v>> FromParam<'v> for Either<A, B> {
406 type Error = (A::Error, B::Error);
407
408 #[inline(always)]
409 fn from_param(param: &'v str) -> Result<Self, Self::Error> {
410 match A::from_param(param) {
411 Ok(a) => Ok(Either::Left(a)),
412 Err(a) => match B::from_param(param) {
413 Ok(b) => Ok(Either::Right(b)),
414 Err(b) => Err((a, b)),
415 },
416 }
417 }
418}