rocket_http/uri/fmt/uri_display.rs
1use std::collections::{BTreeMap, HashMap};
2use std::{fmt, path};
3use std::borrow::Cow;
4
5use time::{macros::format_description, format_description::FormatItem};
6
7use crate::RawStr;
8use crate::uri::fmt::{Part, Path, Query, Formatter};
9
10/// Trait implemented by types that can be displayed as part of a URI in
11/// [`uri!`].
12///
13/// Types implementing this trait can be displayed in a URI-safe manner. Unlike
14/// `Display`, the string written by a `UriDisplay` implementation must be
15/// URI-safe. In practice, this means that the string must either be
16/// percent-encoded or consist only of characters that are alphanumeric, "-",
17/// ".", "_", or "~" - the "unreserved" characters.
18///
19/// # Marker Generic: `Path`, `Query`
20///
21/// The [`Part`] parameter `P` in `UriDisplay<P>` must be either [`Path`] or
22/// [`Query`] (see the [`Part`] documentation for how this is enforced),
23/// resulting in either `UriDisplay<Path>` or `UriDisplay<Query>`.
24///
25/// As the names might imply, the `Path` version of the trait is used when
26/// displaying parameters in the path part of the URI while the `Query` version
27/// is used when displaying parameters in the query part of the URI. These
28/// distinct versions of the trait exist exactly to differentiate, at the
29/// type-level, where in the URI a value is to be written to, allowing for type
30/// safety in the face of differences between the two locations. For example,
31/// while it is valid to use a value of `None` in the query part, omitting the
32/// parameter entirely, doing so is _not_ valid in the path part. By
33/// differentiating in the type system, both of these conditions can be enforced
34/// appropriately through distinct implementations of `UriDisplay<Path>` and
35/// `UriDisplay<Query>`.
36///
37/// Occasionally, the implementation of `UriDisplay` is independent of where the
38/// parameter is to be displayed. When this is the case, the parameter may be
39/// kept generic. That is, implementations can take the form:
40///
41/// ```rust
42/// # extern crate rocket;
43/// # use std::fmt;
44/// # use rocket::http::uri::fmt::{Part, UriDisplay, Formatter};
45/// # struct SomeType;
46/// impl<P: Part> UriDisplay<P> for SomeType
47/// # { fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result { Ok(()) } }
48/// ```
49///
50/// # Code Generation
51///
52/// When the [`uri!`] macro is used to generate a URI for a route, the types for
53/// the route's _path_ URI parameters must implement `UriDisplay<Path>`, while
54/// types in the route's query parameters must implement `UriDisplay<Query>`.
55/// Any parameters ignored with `_` must be of a type that implements
56/// [`Ignorable`]. The `UriDisplay` implementation for these types is used when
57/// generating the URI.
58///
59/// To illustrate `UriDisplay`'s role in code generation for `uri!`, consider
60/// the following route:
61///
62/// ```rust
63/// # #[macro_use] extern crate rocket;
64/// #[get("/item/<id>?<track>")]
65/// fn get_item(id: i32, track: Option<String>) { /* .. */ }
66/// ```
67///
68/// A URI for this route can be generated as follows:
69///
70/// ```rust
71/// # #[macro_use] extern crate rocket;
72/// # type T = ();
73/// # #[get("/item/<id>?<track>")]
74/// # fn get_item(id: i32, track: Option<String>) { /* .. */ }
75/// #
76/// // With unnamed parameters.
77/// uri!(get_item(100, Some("inbound")));
78///
79/// // With named parameters.
80/// uri!(get_item(id = 100, track = Some("inbound")));
81/// uri!(get_item(track = Some("inbound"), id = 100));
82///
83/// // Ignoring `track`.
84/// uri!(get_item(100, _));
85/// uri!(get_item(100, None as Option<String>));
86/// uri!(get_item(id = 100, track = _));
87/// uri!(get_item(track = _, id = 100));
88/// uri!(get_item(id = 100, track = None as Option<&str>));
89/// ```
90///
91/// After verifying parameters and their types, Rocket will generate code
92/// similar (in spirit) to the following:
93///
94/// ```rust
95/// # extern crate rocket;
96/// # use rocket::http::uri::Origin;
97/// # use rocket::http::uri::fmt::{UriDisplay, Path, Query};
98/// #
99/// Origin::parse(&format!("/item/{}?track={}",
100/// &100 as &dyn UriDisplay<Path>, &"inbound" as &dyn UriDisplay<Query>));
101/// ```
102///
103/// For this expression to typecheck, `i32` must implement `UriDisplay<Path>`
104/// and `&str` must implement `UriDisplay<Query>`. What's more, when `track` is
105/// ignored, `Option<String>` is required to implement [`Ignorable`]. As can be
106/// seen, the implementations will be used to display the value in a URI-safe
107/// manner.
108///
109/// [`uri!`]: ../../../../rocket/macro.uri.html
110///
111/// # Provided Implementations
112///
113/// Rocket implements `UriDisplay<P>` for all `P: Part` for several built-in
114/// types.
115///
116/// * **i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32,
117/// f64, bool, IpAddr, Ipv4Addr, Ipv6Addr**
118///
119/// The implementation of `UriDisplay` for these types is identical to the
120/// `Display` implementation.
121///
122/// * **`String`, `&str`, `Cow<str>`**
123///
124/// The string is percent encoded.
125///
126/// * **`&T`, `&mut T`** _where_ **`T: UriDisplay`**
127///
128/// Uses the implementation of `UriDisplay` for `T`.
129///
130/// Rocket implements `UriDisplay<Path>` (but not `UriDisplay<Query>`) for
131/// several built-in types.
132///
133/// * `T` for **`Option<T>`** _where_ **`T: UriDisplay<Path>`**
134///
135/// Uses the implementation of `UriDisplay` for `T::Target`.
136///
137/// When a type of `Option<T>` appears in a route path, use a type of `T` as
138/// the parameter in `uri!`. Note that `Option<T>` itself _does not_
139/// implement `UriDisplay<Path>`.
140///
141/// * `T` for **`Result<T, E>`** _where_ **`T: UriDisplay<Path>`**
142///
143/// Uses the implementation of `UriDisplay` for `T::Target`.
144///
145/// When a type of `Result<T, E>` appears in a route path, use a type of `T`
146/// as the parameter in `uri!`. Note that `Result<T, E>` itself _does not_
147/// implement `UriDisplay<Path>`.
148///
149/// Rocket implements `UriDisplay<Query>` (but not `UriDisplay<Path>`) for
150/// several built-in types.
151///
152/// * **`Form<T>`, `LenientForm<T>`** _where_ **`T: FromUriParam + FromForm`**
153///
154/// Uses the implementation of `UriDisplay` for `T::Target`.
155///
156/// In general, when a type of `Form<T>` is to be displayed as part of a
157/// URI's query, it suffices to derive `UriDisplay` for `T`. Note that any
158/// type that can be converted into a `T` using [`FromUriParam`] can be used
159/// in place of a `Form<T>` in a `uri!` invocation.
160///
161/// * **`Option<T>`** _where_ **`T: UriDisplay<Query>`**
162///
163/// If the `Option` is `Some`, uses the implementation of `UriDisplay` for
164/// `T`. Otherwise, nothing is rendered.
165///
166/// * **`Result<T, E>`** _where_ **`T: UriDisplay<Query>`**
167///
168/// If the `Result` is `Ok`, uses the implementation of `UriDisplay` for
169/// `T`. Otherwise, nothing is rendered.
170///
171/// [`FromUriParam`]: crate::uri::fmt::FromUriParam
172///
173/// # Deriving
174///
175/// Manually implementing `UriDisplay` should be done with care. For most use
176/// cases, deriving `UriDisplay` will suffice:
177///
178/// ```rust
179/// # #[macro_use] extern crate rocket;
180/// # use rocket::http::uri::fmt::{UriDisplay, Query, Path};
181/// // Derives `UriDisplay<Query>`
182/// #[derive(UriDisplayQuery)]
183/// struct User {
184/// name: String,
185/// age: usize,
186/// }
187///
188/// let user = User { name: "Michael Smith".into(), age: 31 };
189/// let uri_string = format!("{}", &user as &dyn UriDisplay<Query>);
190/// assert_eq!(uri_string, "name=Michael%20Smith&age=31");
191///
192/// // Derives `UriDisplay<Path>`
193/// #[derive(UriDisplayPath)]
194/// struct Name(String);
195///
196/// let name = Name("Bob Smith".into());
197/// let uri_string = format!("{}", &name as &dyn UriDisplay<Path>);
198/// assert_eq!(uri_string, "Bob%20Smith");
199/// ```
200///
201/// As long as every field in the structure (or enum for [`UriDisplay<Query>`])
202/// implements `UriDisplay`, the trait can be derived. The implementation calls
203/// [`Formatter::write_named_value()`] for every named field and
204/// [`Formatter::write_value()`] for every unnamed field. See the
205/// [`UriDisplay<Path>`] and [`UriDisplay<Query>`] derive documentation for full
206/// details.
207///
208/// [`Ignorable`]: crate::uri::fmt::Ignorable
209/// [`UriDisplay<Path>`]: ../../../derive.UriDisplayPath.html
210/// [`UriDisplay<Query>`]: ../../../derive.UriDisplayQuery.html
211///
212/// # Implementing
213///
214/// Implementing `UriDisplay` is similar to implementing
215/// [`Display`](std::fmt::Display) with the caveat that extra care must be
216/// taken to ensure that the written string is URI-safe. As mentioned before, in
217/// practice, this means that the string must either be percent-encoded or
218/// consist only of characters that are alphanumeric, "-", ".", "_", or "~".
219///
220/// When manually implementing `UriDisplay` for your types, you should defer to
221/// existing implementations of `UriDisplay` as much as possible. In the example
222/// below, for instance, `Name`'s implementation defers to `String`'s
223/// implementation. To percent-encode a string, use
224/// [`Uri::percent_encode()`](crate::uri::Uri::percent_encode()).
225///
226/// ## Example
227///
228/// The following snippet consists of a `Name` type that implements both
229/// `FromParam` and `UriDisplay<Path>`. The `FromParam` implementation allows
230/// `Name` to be used as the target type of a dynamic parameter, while the
231/// `UriDisplay` implementation allows URIs to be generated for routes with
232/// `Name` as a dynamic path parameter type. Note the custom parsing in the
233/// `FromParam` implementation; as a result of this, a custom (reflexive)
234/// `UriDisplay` implementation is required.
235///
236/// ```rust
237/// # #[macro_use] extern crate rocket;
238/// use rocket::request::FromParam;
239///
240/// struct Name<'r>(&'r str);
241///
242/// const PREFIX: &str = "name:";
243///
244/// impl<'r> FromParam<'r> for Name<'r> {
245/// type Error = &'r str;
246///
247/// /// Validates parameters that start with 'name:', extracting the text
248/// /// after 'name:' as long as there is at least one character.
249/// fn from_param(param: &'r str) -> Result<Self, Self::Error> {
250/// if !param.starts_with(PREFIX) || param.len() < (PREFIX.len() + 1) {
251/// return Err(param);
252/// }
253///
254/// let real_name = ¶m[PREFIX.len()..];
255/// Ok(Name(real_name))
256/// }
257/// }
258///
259/// use std::fmt;
260/// use rocket::http::impl_from_uri_param_identity;
261/// use rocket::http::uri::fmt::{Formatter, FromUriParam, UriDisplay, Path};
262/// use rocket::response::Redirect;
263///
264/// impl UriDisplay<Path> for Name<'_> {
265/// // Writes the raw string `name:`, which is URI-safe, and then delegates
266/// // to the `UriDisplay` implementation for `str` which ensures that
267/// // string is written in a URI-safe manner. In this case, the string will
268/// // be percent encoded.
269/// fn fmt(&self, f: &mut Formatter<Path>) -> fmt::Result {
270/// f.write_raw("name:")?;
271/// UriDisplay::fmt(&self.0, f)
272/// }
273/// }
274///
275/// impl_from_uri_param_identity!([Path] ('a) Name<'a>);
276///
277/// #[get("/name/<name>")]
278/// fn redirector(name: Name<'_>) -> Redirect {
279/// Redirect::to(uri!(real(name)))
280/// }
281///
282/// #[get("/<name>")]
283/// fn real(name: Name<'_>) -> String {
284/// format!("Hello, {}!", name.0)
285/// }
286///
287/// let uri = uri!(real(Name("Mike Smith".into())));
288/// assert_eq!(uri.path(), "/name:Mike%20Smith");
289/// ```
290pub trait UriDisplay<P: Part> {
291 /// Formats `self` in a URI-safe manner using the given formatter.
292 fn fmt(&self, f: &mut Formatter<'_, P>) -> fmt::Result;
293}
294
295impl<P: Part> fmt::Display for &dyn UriDisplay<P> {
296 #[inline(always)]
297 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
298 UriDisplay::fmt(*self, &mut <Formatter<'_, P>>::new(f))
299 }
300}
301
302// Direct implementations: these are the leaves of a call to `UriDisplay::fmt`.
303
304/// Percent-encodes the raw string.
305impl<P: Part> UriDisplay<P> for str {
306 #[inline(always)]
307 fn fmt(&self, f: &mut Formatter<'_, P>) -> fmt::Result {
308 f.write_raw(RawStr::new(self).percent_encode().as_str())
309 }
310}
311
312/// Percent-encodes each segment in the path and normalizes separators.
313impl UriDisplay<Path> for path::Path {
314 fn fmt(&self, f: &mut Formatter<'_, Path>) -> fmt::Result {
315 use std::path::Component;
316
317 for component in self.components() {
318 match component {
319 Component::Prefix(_) | Component::RootDir => continue,
320 _ => f.write_value(&component.as_os_str().to_string_lossy())?
321 }
322 }
323
324 Ok(())
325 }
326}
327
328macro_rules! impl_with_display {
329 ($($T:ty),+ $(,)?) => {$(
330 /// This implementation is identical to the `Display` implementation.
331 impl<P: Part> UriDisplay<P> for $T {
332 #[inline(always)]
333 fn fmt(&self, f: &mut Formatter<'_, P>) -> fmt::Result {
334 use std::fmt::Write;
335 write!(f, "{}", self)
336 }
337 }
338 )+}
339}
340
341use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
342use std::num::{
343 NonZeroIsize, NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128,
344 NonZeroUsize, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128,
345};
346
347// Keep in-sync with the 'FromUriParam' impls.
348impl_with_display! {
349 i8, i16, i32, i64, i128, isize,
350 u8, u16, u32, u64, u128, usize,
351 f32, f64, bool,
352 IpAddr, Ipv4Addr, Ipv6Addr,
353 NonZeroIsize, NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128,
354 NonZeroUsize, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128,
355}
356
357macro_rules! impl_with_string {
358 ($($T:ty => $f:expr),+ $(,)?) => {$(
359 /// This implementation is identical to a percent-encoded version of the
360 /// `Display` implementation.
361 impl<P: Part> UriDisplay<P> for $T {
362 #[inline(always)]
363 fn fmt(&self, f: &mut Formatter<'_, P>) -> fmt::Result {
364 let func: fn(&$T) -> Result<String, fmt::Error> = $f;
365 func(self).and_then(|s| s.as_str().fmt(f))
366 }
367 }
368 )+}
369}
370
371use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
372
373// Keep formats in sync with 'FromFormField' impls.
374static DATE_FMT: &[FormatItem<'_>] = format_description!("[year padding:none]-[month]-[day]");
375static TIME_FMT: &[FormatItem<'_>] = format_description!("[hour padding:none]:[minute]:[second]");
376static DATE_TIME_FMT: &[FormatItem<'_>] =
377 format_description!("[year padding:none]-[month]-[day]T[hour padding:none]:[minute]:[second]");
378
379// Keep list in sync with the 'FromUriParam' impls.
380impl_with_string! {
381 time::Date => |d| d.format(&DATE_FMT).map_err(|_| fmt::Error),
382 time::Time => |d| d.format(&TIME_FMT).map_err(|_| fmt::Error),
383 time::PrimitiveDateTime => |d| d.format(&DATE_TIME_FMT).map_err(|_| fmt::Error),
384 SocketAddr => |a| Ok(a.to_string()),
385 SocketAddrV4 => |a| Ok(a.to_string()),
386 SocketAddrV6 => |a| Ok(a.to_string()),
387}
388
389// These are second level implementations: they all defer to an existing
390// implementation. Keep in-sync with `FromUriParam` impls.
391
392/// Percent-encodes the raw string. Defers to `str`.
393impl<P: Part> UriDisplay<P> for String {
394 #[inline(always)]
395 fn fmt(&self, f: &mut Formatter<'_, P>) -> fmt::Result {
396 self.as_str().fmt(f)
397 }
398}
399
400/// Percent-encodes the raw string. Defers to `str`.
401impl<P: Part> UriDisplay<P> for Cow<'_, str> {
402 #[inline(always)]
403 fn fmt(&self, f: &mut Formatter<'_, P>) -> fmt::Result {
404 self.as_ref().fmt(f)
405 }
406}
407
408/// Percent-encodes each segment in the path and normalizes separators.
409impl UriDisplay<Path> for path::PathBuf {
410 #[inline(always)]
411 fn fmt(&self, f: &mut Formatter<'_, Path>) -> fmt::Result {
412 self.as_path().fmt(f)
413 }
414}
415
416/// Defers to the `UriDisplay<P>` implementation for `T`.
417impl<P: Part, T: UriDisplay<P> + ?Sized> UriDisplay<P> for &T {
418 #[inline(always)]
419 fn fmt(&self, f: &mut Formatter<'_, P>) -> fmt::Result {
420 UriDisplay::fmt(*self, f)
421 }
422}
423
424/// Defers to the `UriDisplay<P>` implementation for `T`.
425impl<P: Part, T: UriDisplay<P> + ?Sized> UriDisplay<P> for &mut T {
426 #[inline(always)]
427 fn fmt(&self, f: &mut Formatter<'_, P>) -> fmt::Result {
428 UriDisplay::fmt(*self, f)
429 }
430}
431
432/// Defers to the `UriDisplay<Query>` implementation for `T`.
433impl<T: UriDisplay<Query>> UriDisplay<Query> for Option<T> {
434 #[inline(always)]
435 fn fmt(&self, f: &mut Formatter<'_, Query>) -> fmt::Result {
436 match self {
437 Some(v) => v.fmt(f),
438 None => Ok(())
439 }
440 }
441}
442
443/// Defers to the `UriDisplay<Query>` implementation for `T`.
444impl<T: UriDisplay<Query>, E> UriDisplay<Query> for Result<T, E> {
445 #[inline(always)]
446 fn fmt(&self, f: &mut Formatter<'_, Query>) -> fmt::Result {
447 match self {
448 Ok(v) => v.fmt(f),
449 Err(_) => Ok(())
450 }
451 }
452}
453
454impl<T: UriDisplay<Query>> UriDisplay<Query> for Vec<T> {
455 fn fmt(&self, f: &mut Formatter<'_, Query>) -> fmt::Result {
456 self.iter().try_for_each(|v| f.write_value(v))
457 }
458}
459
460impl<T: UriDisplay<Query>, const N: usize> UriDisplay<Query> for [T; N] {
461 fn fmt(&self, f: &mut Formatter<'_, Query>) -> fmt::Result {
462 self.iter().try_for_each(|v| f.write_value(v))
463 }
464}
465
466impl UriDisplay<Query> for [u8] {
467 fn fmt(&self, f: &mut Formatter<'_, Query>) -> fmt::Result {
468 f.write_raw(RawStr::percent_encode_bytes(self).as_str())
469 }
470}
471
472impl<K: UriDisplay<Query>, V: UriDisplay<Query>> UriDisplay<Query> for HashMap<K, V> {
473 fn fmt(&self, f: &mut Formatter<'_, Query>) -> fmt::Result {
474 use std::fmt::Write;
475
476 let mut field_name = String::with_capacity(8);
477 for (i, (key, value)) in self.iter().enumerate() {
478 field_name.truncate(0);
479 write!(field_name, "k:{}", i)?;
480 f.write_named_value(&field_name, key)?;
481
482 field_name.replace_range(..1, "v");
483 f.write_named_value(&field_name, value)?;
484 }
485
486 Ok(())
487 }
488}
489
490impl<K: UriDisplay<Query>, V: UriDisplay<Query>> UriDisplay<Query> for BTreeMap<K, V> {
491 fn fmt(&self, f: &mut Formatter<'_, Query>) -> fmt::Result {
492 use std::fmt::Write;
493
494 let mut field_name = String::with_capacity(8);
495 for (i, (key, value)) in self.iter().enumerate() {
496 field_name.truncate(0);
497 write!(field_name, "k:{}", i)?;
498 f.write_named_value(&field_name, key)?;
499
500 field_name.replace_range(..1, "v");
501 f.write_named_value(&field_name, value)?;
502 }
503
504 Ok(())
505 }
506}
507
508#[cfg(feature = "uuid")] impl_with_display!(uuid_::Uuid);
509#[cfg(feature = "uuid")] crate::impl_from_uri_param_identity!(uuid_::Uuid);
510
511// And finally, the `Ignorable` trait, which has sugar of `_` in the `uri!`
512// macro, which expands to a typecheck.
513
514/// Trait implemented by types that can be ignored in `uri!`.
515///
516/// When a parameter is explicitly ignored in `uri!` by supplying `_` as the
517/// parameter's value, that parameter's type is required to implement this
518/// trait for the corresponding `Part`.
519///
520/// ```rust
521/// # #[macro_use] extern crate rocket;
522/// #[get("/item/<id>?<track>")]
523/// fn get_item(id: i32, track: Option<u8>) { /* .. */ }
524///
525/// // Ignore the `track` parameter: `Option<u8>` must be `Ignorable`.
526/// uri!(get_item(100, _));
527/// uri!(get_item(id = 100, track = _));
528///
529/// // Provide a value for `track`.
530/// uri!(get_item(100, Some(4)));
531/// uri!(get_item(id = 100, track = Some(4)));
532/// ```
533///
534/// # Implementations
535///
536/// Only `Option<T>` and `Result<T, E>` implement this trait. You may implement
537/// this trait for your own ignorable types as well:
538///
539/// ```rust
540/// # #[macro_use] extern crate rocket;
541/// use rocket::http::uri::fmt::{Ignorable, Query};
542///
543/// # struct MyType;
544/// impl Ignorable<Query> for MyType { }
545/// ```
546pub trait Ignorable<P: Part> { }
547
548impl<T> Ignorable<Query> for Option<T> { }
549impl<T, E> Ignorable<Query> for Result<T, E> { }
550
551#[doc(hidden)]
552pub fn assert_ignorable<P: Part, T: Ignorable<P>>() { }
553
554#[cfg(test)]
555mod uri_display_tests {
556 use std::path;
557 use crate::uri::fmt::{FromUriParam, UriDisplay};
558 use crate::uri::fmt::{Query, Path};
559
560 macro_rules! uri_display {
561 (<$P:ident, $Target:ty> $source:expr) => ({
562 let tmp = $source;
563 let target = <$Target as FromUriParam<$P, _>>::from_uri_param(tmp);
564 format!("{}", &target as &dyn UriDisplay<$P>)
565 })
566 }
567
568 macro_rules! assert_display {
569 (<$P:ident, $Target:ty> $source:expr, $expected:expr) => ({
570 assert_eq!(uri_display!(<$P, $Target> $source), $expected);
571 })
572 }
573
574 #[test]
575 fn uri_display_encoding() {
576 assert_display!(<Query, String> "hello", "hello");
577 assert_display!(<Query, String> "hi hi", "hi%20hi");
578 assert_display!(<Query, &str> "hi hi", "hi%20hi");
579 assert_display!(<Query, &str> &"hi hi", "hi%20hi");
580 assert_display!(<Query, usize> 10, "10");
581 assert_display!(<Query, u8> 10, "10");
582 assert_display!(<Query, i32> 10, "10");
583 assert_display!(<Query, isize> 10, "10");
584
585 assert_display!(<Path, String> "hello", "hello");
586 assert_display!(<Path, String> "hi hi", "hi%20hi");
587 assert_display!(<Path, &str> "hi hi", "hi%20hi");
588 assert_display!(<Path, &str> &"hi hi", "hi%20hi");
589 assert_display!(<Path, usize> 10, "10");
590 assert_display!(<Path, u8> 10, "10");
591 assert_display!(<Path, i32> 10, "10");
592 assert_display!(<Path, isize> 10, "10");
593
594 assert_display!(<Query, &str> &"hi there", "hi%20there");
595 assert_display!(<Query, isize> &10, "10");
596 assert_display!(<Query, u8> &10, "10");
597
598 assert_display!(<Path, &str> &"hi there", "hi%20there");
599 assert_display!(<Path, isize> &10, "10");
600 assert_display!(<Path, u8> &10, "10");
601
602 assert_display!(<Path, Option<&str>> &"hi there", "hi%20there");
603 assert_display!(<Path, Option<isize>> &10, "10");
604 assert_display!(<Path, Option<u8>> &10, "10");
605 assert_display!(<Query, Option<&str>> Some(&"hi there"), "hi%20there");
606 assert_display!(<Query, Option<isize>> Some(&10), "10");
607 assert_display!(<Query, Option<u8>> Some(&10), "10");
608
609 assert_display!(<Path, Result<&str, usize>> &"hi there", "hi%20there");
610 assert_display!(<Path, Result<isize, &str>> &10, "10");
611 assert_display!(<Path, Result<u8, String>> &10, "10");
612 assert_display!(<Query, Result<&str, usize>> Ok(&"hi there"), "hi%20there");
613 assert_display!(<Query, Result<isize, &str>> Ok(&10), "10");
614 assert_display!(<Query, Result<u8, String>> Ok(&10), "10");
615 }
616
617 #[test]
618 fn paths() {
619 assert_display!(<Path, path::PathBuf> "hello", "hello");
620 assert_display!(<Path, path::PathBuf> "hi there", "hi%20there");
621 assert_display!(<Path, path::PathBuf> "hello/world", "hello/world");
622 assert_display!(<Path, path::PathBuf> "hello//world", "hello/world");
623 assert_display!(<Path, path::PathBuf> "hello/ world", "hello/%20world");
624
625 assert_display!(<Path, path::PathBuf> "hi/wo rld", "hi/wo%20rld");
626
627 assert_display!(<Path, path::PathBuf> &"hi/wo rld", "hi/wo%20rld");
628 assert_display!(<Path, path::PathBuf> &"hi there", "hi%20there");
629 }
630
631 struct Wrapper<T>(T);
632
633 impl<A, T: FromUriParam<Query, A>> FromUriParam<Query, A> for Wrapper<T> {
634 type Target = T::Target;
635
636 #[inline(always)]
637 fn from_uri_param(param: A) -> Self::Target {
638 T::from_uri_param(param)
639 }
640 }
641
642 impl FromUriParam<Path, usize> for Wrapper<usize> {
643 type Target = usize;
644
645 #[inline(always)]
646 fn from_uri_param(param: usize) -> Self::Target {
647 param
648 }
649 }
650
651 #[test]
652 fn uri_display_encoding_wrapped() {
653 assert_display!(<Query, Option<Wrapper<&str>>> Some(&"hi there"), "hi%20there");
654 assert_display!(<Query, Option<Wrapper<&str>>> Some("hi there"), "hi%20there");
655
656 assert_display!(<Query, Option<Wrapper<isize>>> Some(10), "10");
657 assert_display!(<Query, Option<Wrapper<usize>>> Some(18), "18");
658 assert_display!(<Path, Option<Wrapper<usize>>> 238, "238");
659
660 assert_display!(<Path, Result<Option<Wrapper<usize>>, usize>> 238, "238");
661 assert_display!(<Path, Option<Result<Wrapper<usize>, usize>>> 123, "123");
662 }
663
664 #[test]
665 fn check_ignorables() {
666 use crate::uri::fmt::assert_ignorable;
667
668 assert_ignorable::<Query, Option<usize>>();
669 assert_ignorable::<Query, Option<Wrapper<usize>>>();
670 assert_ignorable::<Query, Result<Wrapper<usize>, usize>>();
671 assert_ignorable::<Query, Option<Result<Wrapper<usize>, usize>>>();
672 assert_ignorable::<Query, Result<Option<Wrapper<usize>>, usize>>();
673 }
674}