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&lt;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&lt;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 => (&param[..i], &param[(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}