rocket/request/
from_param.rs

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