rocket/request/
from_param.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
use std::str::FromStr;
use std::path::PathBuf;

use crate::error::Empty;
use crate::either::Either;
use crate::http::uri::{Segments, error::PathError, fmt::Path};

/// Trait to convert a dynamic path segment string to a concrete value.
///
/// This trait is used by Rocket's code generation facilities to parse dynamic
/// path segment string values into a given type. That is, when a path contains
/// a dynamic segment `<param>` where `param` has some type `T` that implements
/// `FromParam`, `T::from_param` will be called.
///
/// # Deriving
///
/// The `FromParam` trait can be automatically derived for C-like enums. See
/// [`FromParam` derive](macro@rocket::FromParam) for more information.
///
/// # Forwarding
///
/// If the conversion fails, the incoming request will be forwarded to the next
/// matching route, if any. For instance, consider the following route and
/// handler for the dynamic `"/<id>"` path:
///
/// ```rust
/// # #[macro_use] extern crate rocket;
/// #[get("/<id>")]
/// fn hello(id: usize) -> String {
/// # let _id = id;
/// # /*
///     ...
/// # */
/// # "".to_string()
/// }
/// # fn main() {  }
/// ```
///
/// If `usize::from_param` returns an `Ok(usize)` variant, the encapsulated
/// value is used as the `id` function parameter. If not, the request is
/// forwarded to the next matching route. Since there are no additional matching
/// routes, this example will result in a 422 error for requests with invalid
/// `id` values.
///
/// # Catching Errors
///
/// Sometimes, a forward is not desired, and instead, we simply want to know
/// that the dynamic path segment could not be parsed into some desired type
/// `T`. In these cases, types of `Option<T>`, `Result<T, T::Error>`, or
/// `Either<A, B>` can be used, which implement `FromParam` themselves.
///
///   * **`Option<T>`** _where_ **`T: FromParam`**
///
///     Always returns successfully.
///
///     If the conversion to `T` fails, `None` is returned. If the conversion
///     succeeds, `Some(value)` is returned.
///
///   * **`Result<T, T::Error>`** _where_ **`T: FromParam`**
///
///     Always returns successfully.
///
///     If the conversion to `T` fails, `Err(error)` is returned. If the
///     conversion succeeds, `Ok(value)` is returned.
///
///   * **`Either<A, B>`** _where_ **`A: FromParam`** _and_ **`B: FromParam`**
///
///      Fails only when both `A::from_param` and `B::from_param` fail. If one
///      of the two succeeds, the successful value is returned in
///      `Either::Left(A)` or `Either::Right(B)` variant, respectively. If both
///      fail, the error values from both calls are returned in a tuple in the
///      `Err` variant.
///
/// `Either<A, B>` is particularly useful with a `B` type of `&str`, allowing
/// you to retrieve the invalid path segment. Because `&str`'s implementation of
/// `FromParam` always succeeds, the `Right` variant of the `Either` will always
/// contain the path segment in case of failure.
///
/// For instance, consider the following route and handler:
///
/// ```rust
/// # #[macro_use] extern crate rocket;
/// use rocket::either::{Either, Left, Right};
///
/// #[get("/<id>")]
/// fn hello(id: Either<usize, &str>) -> String {
///     match id {
///         Left(id_num) => format!("usize: {}", id_num),
///         Right(string) => format!("Not a usize: {}", string)
///     }
/// }
/// # fn main() {  }
/// ```
///
/// In the above example, if the dynamic path segment cannot be parsed into a
/// `usize`, the raw path segment is returned in the `Right` variant of the
/// `Either<usize, &str>` value.
///
/// # Provided Implementations
///
/// Rocket implements `FromParam` for several standard library types. Their
/// behavior is documented here.
///
///   *
///       * Primitive types: **f32, f64, isize, i8, i16, i32, i64, i128,
///         usize, u8, u16, u32, u64, u128, bool**
///       * `IpAddr` and `SocketAddr` types: **IpAddr, Ipv4Addr, Ipv6Addr,
///         SocketAddrV4, SocketAddrV6, SocketAddr**
///       * `NonZero*` types: **NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64,
///         NonZeroI128, NonZeroIsize, NonZeroU8, NonZeroU16, NonZeroU32,
///         NonZeroU64, NonZeroU128, NonZeroUsize**
///
///     A value is parsed successfully if the `from_str` method from the given
///     type returns successfully. Otherwise, the raw path segment is returned
///     in the `Err` value.
///
///   * **&str, String**
///
///     _This implementation always returns successfully._
///
///     Returns the percent-decoded path segment with invalid UTF-8 byte
///     sequences replaced by � U+FFFD.
///
///   * **Option&lt;T>** _where_ **T: FromParam**
///
///     _This implementation always returns successfully._
///
///     The path segment is parsed by `T`'s `FromParam` implementation. If the
///     parse succeeds, a `Some(parsed_value)` is returned. Otherwise, a `None`
///     is returned.
///
///   * **Result&lt;T, T::Error>** _where_ **T: FromParam**
///
///     _This implementation always returns successfully._
///
///     The path segment is parsed by `T`'s `FromParam` implementation. The
///     returned `Result` value is returned.
///
/// # Example
///
/// Say you want to parse a segment of the form:
///
/// ```text
/// [a-zA-Z]+:[0-9]+
/// ```
///
/// into the following structure, where the string before the `:` is stored in
/// `key` and the number after the colon is stored in `value`:
///
/// ```rust
/// struct MyParam<'r> {
///     key: &'r str,
///     value: usize
/// }
/// ```
///
/// The following implementation accomplishes this:
///
/// ```rust
/// use rocket::request::FromParam;
/// # #[allow(dead_code)]
/// # struct MyParam<'r> { key: &'r str, value: usize }
///
/// impl<'r> FromParam<'r> for MyParam<'r> {
///     type Error = &'r str;
///
///     fn from_param(param: &'r str) -> Result<Self, Self::Error> {
///         // We can convert `param` into a `str` since we'll check every
///         // character for safety later.
///         let (key, val_str) = match param.find(':') {
///             Some(i) if i > 0 => (&param[..i], &param[(i + 1)..]),
///             _ => return Err(param)
///         };
///
///         if !key.chars().all(|c| c.is_ascii_alphabetic()) {
///             return Err(param);
///         }
///
///         val_str.parse()
///             .map(|value| MyParam { key, value })
///             .map_err(|_| param)
///     }
/// }
/// ```
///
/// With the implementation, the `MyParam` type can be used as the target of a
/// dynamic path segment:
///
/// ```rust
/// # #[macro_use] extern crate rocket;
/// # use rocket::request::FromParam;
/// # #[allow(dead_code)]
/// # struct MyParam<'r> { key: &'r str, value: usize }
/// # impl<'r> FromParam<'r> for MyParam<'r> {
/// #     type Error = &'r str;
/// #     fn from_param(param: &'r str) -> Result<Self, Self::Error> {
/// #         Err(param)
/// #     }
/// # }
/// #
/// #[get("/<key_val>")]
/// fn hello(key_val: MyParam) -> String {
/// # let _kv = key_val;
/// # /*
///     ...
/// # */
/// # "".to_string()
/// }
/// # fn main() {  }
/// ```
pub trait FromParam<'a>: Sized {
    /// The associated error to be returned if parsing/validation fails.
    type Error: std::fmt::Debug;

    /// Parses and validates an instance of `Self` from a path parameter string
    /// or returns an `Error` if parsing or validation fails.
    fn from_param(param: &'a str) -> Result<Self, Self::Error>;
}

impl<'a> FromParam<'a> for &'a str {
    type Error = Empty;

    #[inline(always)]
    fn from_param(param: &'a str) -> Result<&'a str, Self::Error> {
        if param.is_empty() {
            return Err(Empty);
        }

        Ok(param)
    }
}

impl<'a> FromParam<'a> for String {
    type Error = Empty;

    #[track_caller]
    #[inline(always)]
    fn from_param(param: &'a str) -> Result<String, Self::Error> {
        #[cfg(debug_assertions)] {
            let location = std::panic::Location::caller();
            warn!(%location, "`String` as a parameter is inefficient. Use `&str` instead.");
        }

        if param.is_empty() {
            return Err(Empty);
        }

        Ok(param.to_string())
    }
}

macro_rules! impl_with_fromstr {
    ($($T:ty),+) => ($(
        impl<'a> FromParam<'a> for $T {
            type Error = <$T as FromStr>::Err;

            #[inline(always)]
            fn from_param(param: &'a str) -> Result<Self, Self::Error> {
                <$T as FromStr>::from_str(param)
            }
        }
    )+)
}

use std::num::{
    NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize,
    NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize,
};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr};

impl_with_fromstr! {
    i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64,
    NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize,
    NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize,
    bool, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr
}

impl<'a> FromParam<'a> for PathBuf {
    type Error = PathError;

    #[inline]
    fn from_param(param: &'a str) -> Result<Self, Self::Error> {
        use crate::http::private::Indexed;

        let segments = &[Indexed::Indexed(0, param.len())];
        Segments::new(param.into(), segments).to_path_buf(false)
    }
}

impl<'a, T: FromParam<'a>> FromParam<'a> for Result<T, T::Error> {
    type Error = std::convert::Infallible;

    #[inline]
    fn from_param(param: &'a str) -> Result<Self, Self::Error> {
        match T::from_param(param) {
            Ok(val) => Ok(Ok(val)),
            Err(e) => Ok(Err(e)),
        }
    }
}

impl<'a, T: FromParam<'a>> FromParam<'a> for Option<T> {
    type Error = std::convert::Infallible;

    #[inline]
    fn from_param(param: &'a str) -> Result<Self, Self::Error> {
        match T::from_param(param) {
            Ok(val) => Ok(Some(val)),
            Err(_) => Ok(None)
        }
    }
}

/// Trait to convert _many_ dynamic path segment strings to a concrete value.
///
/// This is the `..` analog to [`FromParam`], and its functionality is identical
/// to it with one exception: this trait applies to segment parameters of the
/// form `<param..>`, where `param` is of some type `T` that implements
/// `FromSegments`. `T::from_segments` is called to convert the matched segments
/// (via the [`Segments`] iterator) into the implementing type.
///
/// # Provided Implementations
///
/// **`PathBuf`**
///
/// The `PathBuf` implementation constructs a path from the segments iterator.
/// Each segment is percent-decoded. If a segment equals ".." before or after
/// decoding, the previous segment (if any) is omitted. For security purposes,
/// any other segments that begin with "*" or "." are ignored.  If a
/// percent-decoded segment results in invalid UTF8, an `Err` is returned with
/// the `Utf8Error`.
pub trait FromSegments<'r>: Sized {
    /// The associated error to be returned when parsing fails.
    type Error: std::fmt::Debug;

    /// Parses an instance of `Self` from many dynamic path parameter strings or
    /// returns an `Error` if one cannot be parsed.
    fn from_segments(segments: Segments<'r, Path>) -> Result<Self, Self::Error>;
}

impl<'r> FromSegments<'r> for Segments<'r, Path> {
    type Error = std::convert::Infallible;

    #[inline(always)]
    fn from_segments(segments: Self) -> Result<Self, Self::Error> {
        Ok(segments)
    }
}

/// Creates a `PathBuf` from a `Segments` iterator. The returned `PathBuf` is
/// percent-decoded. If a segment is equal to "..", the previous segment (if
/// any) is skipped.
///
/// For security purposes, if a segment meets any of the following conditions,
/// an `Err` is returned indicating the condition met:
///
///   * Decoded segment starts with any of: `.` (except `..`), `*`
///   * Decoded segment ends with any of: `:`, `>`, `<`
///   * Decoded segment contains any of: `/`
///   * On Windows, decoded segment contains any of: `\`
///   * Percent-encoding results in invalid UTF8.
///
/// As a result of these conditions, a `PathBuf` derived via `FromSegments` is
/// safe to interpolate within, or use as a suffix of, a path without additional
/// checks.
impl FromSegments<'_> for PathBuf {
    type Error = PathError;

    fn from_segments(segments: Segments<'_, Path>) -> Result<Self, Self::Error> {
        segments.to_path_buf(false)
    }
}

impl<'r, T: FromSegments<'r>> FromSegments<'r> for Result<T, T::Error> {
    type Error = std::convert::Infallible;

    #[inline]
    fn from_segments(segments: Segments<'r, Path>) -> Result<Result<T, T::Error>, Self::Error> {
        match T::from_segments(segments) {
            Ok(val) => Ok(Ok(val)),
            Err(e) => Ok(Err(e)),
        }
    }
}

impl<'r, T: FromSegments<'r>> FromSegments<'r> for Option<T> {
    type Error = std::convert::Infallible;

    #[inline]
    fn from_segments(segments: Segments<'r, Path>) -> Result<Option<T>, Self::Error> {
        match T::from_segments(segments) {
            Ok(val) => Ok(Some(val)),
            Err(_) => Ok(None)
        }
    }
}

/// Implements `FromParam` for `Either<A, B>`, where `A` and `B` both implement
/// `FromParam`. If `A::from_param` returns `Ok(a)`, `Either::Left(a)` is
/// returned. If `B::from_param` returns `Ok(b)`, `Either::Right(b)` is
/// returned. If both `A::from_param` and `B::from_param` return `Err(a)` and
/// `Err(b)`, respectively, then `Err((a, b))` is returned.
impl<'v, A: FromParam<'v>, B: FromParam<'v>> FromParam<'v> for Either<A, B> {
    type Error = (A::Error, B::Error);

    #[inline(always)]
    fn from_param(param: &'v str) -> Result<Self, Self::Error> {
        match A::from_param(param) {
            Ok(a) => Ok(Either::Left(a)),
            Err(a) => match B::from_param(param) {
                Ok(b) => Ok(Either::Right(b)),
                Err(b) => Err((a, b)),
            }
        }
    }
}