Skip to content

Commit

Permalink
Allow displaying [T; N], Vec<T>, [u8] via 'uri!'.
Browse files Browse the repository at this point in the history
Resolves #2750.
  • Loading branch information
SergioBenitez committed Mar 14, 2024
1 parent 7c702a5 commit 50c44e8
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 8 deletions.
36 changes: 36 additions & 0 deletions core/codegen/tests/typed-uris.rs
Original file line number Diff line number Diff line change
Expand Up @@ -673,3 +673,39 @@ fn test_route_uri_normalization_with_prefix() {
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",
}
}
33 changes: 29 additions & 4 deletions core/http/src/uri/fmt/from_uri_param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,16 @@ use crate::uri::fmt::{self, Part};
///
/// In the rare case that `UriDisplay` is implemented manually, this trait, too,
/// 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:
///
/// * `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 mut T> for T`
///
/// See [`impl_from_uri_param_identity!`](crate::impl_from_uri_param_identity!)
/// for usage details.
/// See [`impl_from_uri_param_identity!`] for usage details.
///
/// [`impl_from_uri_param_identity!`]: crate::impl_from_uri_param_identity!
///
/// # Code Generation
///
Expand Down Expand Up @@ -316,7 +317,31 @@ impl_from_uri_param_identity!([fmt::Path] PathBuf);

impl_conversion_ref! {
[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`.
Expand Down
16 changes: 12 additions & 4 deletions core/http/src/uri/fmt/uri_display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,11 +453,19 @@ impl<T: UriDisplay<Query>, E> UriDisplay<Query> for Result<T, E> {

impl<T: UriDisplay<Query>> UriDisplay<Query> for Vec<T> {
fn fmt(&self, f: &mut Formatter<'_, Query>) -> fmt::Result {
for value in self {
f.write_value(value)?;
}
self.iter().try_for_each(|v| f.write_value(v))
}
}

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())
}
}

Expand Down

0 comments on commit 50c44e8

Please sign in to comment.