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