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