Allow displaying [T; N], Vec<T>, [u8] via 'uri!'.

Resolves #2750.
This commit is contained in:
Sergio Benitez 2024-03-14 15:24:34 -07:00
parent 7c702a5b01
commit 50c44e8fdc
3 changed files with 77 additions and 8 deletions

View File

@ -673,3 +673,39 @@ fn test_route_uri_normalization_with_prefix() {
uri!("/foo/bar/", world()) => "/foo/bar/world", uri!("/foo/bar/", world()) => "/foo/bar/world",
} }
} }
#[test]
fn test_vec_in_query() {
#[post("/?<v>")]
fn f(v: Vec<usize>) { }
#[post("/?<v>")]
fn g(v: Vec<String>) { }
#[post("/?<v>")]
fn h(v: &[u8]) { }
let bytes = vec![0u8, 1, 2];
assert_uri_eq! {
uri!(f(v = vec![1, 2, 3])) => "/?v=1&v=2&v=3",
uri!(f(v = &vec![1, 2, 3])) => "/?v=1&v=2&v=3",
uri!(f(v = &[1, 2, 3])) => "/?v=1&v=2&v=3",
uri!(f(v = [1, 2, 3])) => "/?v=1&v=2&v=3",
uri!(f(vec![1, 2, 3])) => "/?v=1&v=2&v=3",
uri!(f(&vec![1, 2, 3])) => "/?v=1&v=2&v=3",
uri!(f(&[1, 2, 3])) => "/?v=1&v=2&v=3",
uri!(f([1, 2, 3])) => "/?v=1&v=2&v=3",
// TODO: Introduce `RawBytes` + FromUriParam + UriDisplay impls.
// uri!(f(v = &[1, 2, 3][..])) => "/?v=1&v=2&v=3",
uri!(g(v = vec!["a", "b=c", "d"])) => "/?v=a&v=b%3Dc&v=d",
uri!(g(v = &vec!["a", "b=c", "d"])) => "/?v=a&v=b%3Dc&v=d",
uri!(g(v = ["a", "b=c", "d"])) => "/?v=a&v=b%3Dc&v=d",
uri!(g(v = &["a", "b=c", "d"])) => "/?v=a&v=b%3Dc&v=d",
uri!(h(v = bytes.as_slice())) => "/?v=%00%01%02",
uri!(h(v = &[1, 2, 3][..])) => "/?v=%01%02%03",
}
}

View File

@ -16,15 +16,16 @@ use crate::uri::fmt::{self, Part};
/// ///
/// In the rare case that `UriDisplay` is implemented manually, this trait, too, /// In the rare case that `UriDisplay` is implemented manually, this trait, too,
/// must be implemented explicitly. In the majority of cases, implementation can /// must be implemented explicitly. In the majority of cases, implementation can
/// be automated. Rocket provides [`impl_from_uri_param_identity`] to generate /// be automated. Rocket provides [`impl_from_uri_param_identity!`] to generate
/// the _identity_ implementations automatically. For a type `T`, these are: /// the _identity_ implementations automatically. For a type `T`, these are:
/// ///
/// * `impl<P: Part> FromUriParam<P, T> for T` /// * `impl<P: Part> FromUriParam<P, T> for T`
/// * `impl<'x, P: Part> FromUriParam<P, &'x T> for T` /// * `impl<'x, P: Part> FromUriParam<P, &'x T> for T`
/// * `impl<'x, P: Part> FromUriParam<P, &'x mut T> for T` /// * `impl<'x, P: Part> FromUriParam<P, &'x mut T> for T`
/// ///
/// See [`impl_from_uri_param_identity!`](crate::impl_from_uri_param_identity!) /// See [`impl_from_uri_param_identity!`] for usage details.
/// for usage details. ///
/// [`impl_from_uri_param_identity!`]: crate::impl_from_uri_param_identity!
/// ///
/// # Code Generation /// # Code Generation
/// ///
@ -316,7 +317,31 @@ impl_from_uri_param_identity!([fmt::Path] PathBuf);
impl_conversion_ref! { impl_conversion_ref! {
[fmt::Path] ('a) &'a Path => PathBuf, [fmt::Path] ('a) &'a Path => PathBuf,
[fmt::Path] ('a) PathBuf => &'a Path [fmt::Path] ('a) PathBuf => &'a Path,
}
// TODO: A specialized `RawBytes` instead of `&[u8]`. Then impl [T] => Vec<T>.
impl_from_uri_param_identity!([fmt::Query] ('a) &'a [u8]);
impl_conversion_ref! {
[fmt::Query] (T, A: FromUriParam<fmt::Query, T> + UriDisplay<fmt::Query>) Vec<A> => Vec<T>,
[fmt::Query] (
T,
A: FromUriParam<fmt::Query, T> + UriDisplay<fmt::Query>,
const N: usize
) Vec<A> => [T; N],
[fmt::Query] (
T,
A: FromUriParam<fmt::Query, T> + UriDisplay<fmt::Query>,
const N: usize
) [A; N] => Vec<T>,
[fmt::Query] (
T,
A: FromUriParam<fmt::Query, T> + UriDisplay<fmt::Query>,
const N: usize
) [A; N] => [T; N],
} }
/// A no cost conversion allowing an `&str` to be used in place of a `PathBuf`. /// A no cost conversion allowing an `&str` to be used in place of a `PathBuf`.

View File

@ -453,11 +453,19 @@ impl<T: UriDisplay<Query>, E> UriDisplay<Query> for Result<T, E> {
impl<T: UriDisplay<Query>> UriDisplay<Query> for Vec<T> { impl<T: UriDisplay<Query>> UriDisplay<Query> for Vec<T> {
fn fmt(&self, f: &mut Formatter<'_, Query>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_, Query>) -> fmt::Result {
for value in self { self.iter().try_for_each(|v| f.write_value(v))
f.write_value(value)?; }
} }
Ok(()) impl<T: UriDisplay<Query>, const N: usize> UriDisplay<Query> for [T; N] {
fn fmt(&self, f: &mut Formatter<'_, Query>) -> fmt::Result {
self.iter().try_for_each(|v| f.write_value(v))
}
}
impl UriDisplay<Query> for [u8] {
fn fmt(&self, f: &mut Formatter<'_, Query>) -> fmt::Result {
f.write_raw(RawStr::percent_encode_bytes(self).as_str())
} }
} }