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<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<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 => (¶m[..i], ¶m[(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}