diff --git a/core/codegen/tests/typed-uris.rs b/core/codegen/tests/typed-uris.rs index ab064dc8..3ffb6401 100644 --- a/core/codegen/tests/typed-uris.rs +++ b/core/codegen/tests/typed-uris.rs @@ -537,3 +537,37 @@ fn test_simple_ignored() { uri!(ignore_with_q3(&mut 5, "boo", "hi b", "ho")) => "/hi/5/foo/boo?hi=hi%20b&hey=ho", } } + +#[test] +fn test_maps() { + use std::collections::{HashMap, BTreeMap}; + use rocket::figment::util::map; + + #[get("/?")] fn hmap(mut bar: HashMap) { + let _ = uri!(bmap(&bar)); + let _ = uri!(bmap(&mut bar)); + let _ = uri!(bmap(bar)); + } + + assert_uri_eq! { + uri!(hmap(map!["foo" => 10])) => "/?bar.k:0=foo&bar.v:0=10", + uri!(hmap(map!["foo".to_string() => 10])) => "/?bar.k:0=foo&bar.v:0=10", + uri!(hmap(&map!["foo".to_string() => 10])) => "/?bar.k:0=foo&bar.v:0=10", + uri!(hmap(&mut map!["foo".to_string() => 10])) => "/?bar.k:0=foo&bar.v:0=10", + uri!(hmap(&map!["foo" => 10])) => "/?bar.k:0=foo&bar.v:0=10", + } + + #[get("/?")] fn bmap(mut bar: BTreeMap<&str, usize>) { + let _ = uri!(hmap(&bar)); + let _ = uri!(hmap(&mut bar)); + let _ = uri!(hmap(bar)); + } + + assert_uri_eq! { + uri!(bmap(map!["foo" => 10])) => "/?bar.k:0=foo&bar.v:0=10", + uri!(bmap(map!["foo".to_string() => 10])) => "/?bar.k:0=foo&bar.v:0=10", + uri!(bmap(&map!["foo".to_string() => 10])) => "/?bar.k:0=foo&bar.v:0=10", + uri!(bmap(&mut map!["foo".to_string() => 10])) => "/?bar.k:0=foo&bar.v:0=10", + uri!(bmap(&map!["foo" => 10])) => "/?bar.k:0=foo&bar.v:0=10", + } +} diff --git a/core/http/src/uri/fmt/from_uri_param.rs b/core/http/src/uri/fmt/from_uri_param.rs index 9caadd69..0850b905 100644 --- a/core/http/src/uri/fmt/from_uri_param.rs +++ b/core/http/src/uri/fmt/from_uri_param.rs @@ -1,3 +1,4 @@ +use std::collections::{BTreeMap, HashMap}; use std::path::{Path, PathBuf}; use crate::uri::fmt::UriDisplay; @@ -191,25 +192,28 @@ pub trait FromUriParam { fn from_uri_param(param: T) -> Self::Target; } -use std::{borrow::Cow, net::{IpAddr, Ipv4Addr, Ipv6Addr}}; - #[doc(hidden)] #[macro_export(local_inner_macros)] macro_rules! impl_conversion_ref { - ($(($($l:tt)+) $A:ty => $B:ty),*) => ( impl_conversion_ref!(@_ $(($($l)+,) $A => $B),*); ); - ($($A:ty => $B:ty),*) => ( impl_conversion_ref!(@_ $(() $A => $B),*); ); + ($(($($l:tt)+) $A:ty => $B:ty),* $(,)?) => ( + impl_conversion_ref!(@_ $(($($l)+,) $A => $B),*); + ); - (@_ $(($($l:tt)*) $A:ty => $B:ty),*) => ($( + ($($A:ty => $B:ty),* $(,)?) => ( + impl_conversion_ref!(@_ $(() $A => $B),*); + ); + + (@_ $(($($l:tt)*) $A:ty => $B:ty),* $(,)?) => ($( impl_conversion_ref!([P] ($($l)* P: $crate::uri::fmt::Part) $A => $B); )*); - ($([$P:ty] ($($l:tt)*) $A:ty => $B:ty),*) => ($( + ($([$P:ty] ($($l:tt)*) $A:ty => $B:ty),* $(,)?) => ($( impl_conversion_ref!(@_ [$P] ($($l)*) $A => $B); impl_conversion_ref!(@_ [$P] ('x, $($l)*) &'x $A => $B); impl_conversion_ref!(@_ [$P] ('x, $($l)*) &'x mut $A => $B); )*); - ($([$P:ty] $A:ty => $B:ty),*) => ( impl_conversion_ref!($([$P] () $A => $B),*);); + ($([$P:ty] $A:ty => $B:ty),* $(,)?) => ( impl_conversion_ref!($([$P] () $A => $B),*);); (@_ [$P:ty] ($($l:tt)*) $A:ty => $B:ty) => ( impl<$($l)*> $crate::uri::fmt::FromUriParam<$P, $A> for $B { @@ -272,18 +276,28 @@ macro_rules! impl_conversion_ref { /// [`Query`]: crate::uri::fmt::Query #[macro_export(local_inner_macros)] macro_rules! impl_from_uri_param_identity { - ($(($($l:tt)*) $T:ty),*) => ($( impl_conversion_ref!(($($l)*) $T => $T); )*); - ($([$P:ty] ($($l:tt)*) $T:ty),*) => ($( impl_conversion_ref!([$P] ($($l)*) $T => $T); )*); - ($([$P:ty] $T:ty),*) => ($( impl_conversion_ref!([$P] $T => $T); )*); - ($($T:ty),*) => ($( impl_conversion_ref!($T => $T); )*); + ($(($($l:tt)*) $T:ty),* $(,)?) => ($( impl_conversion_ref!(($($l)*) $T => $T); )*); + ($([$P:ty] ($($l:tt)*) $T:ty),* $(,)?) => ($( impl_conversion_ref!([$P] ($($l)*) $T => $T); )*); + ($([$P:ty] $T:ty),* $(,)?) => ($( impl_conversion_ref!([$P] $T => $T); )*); + ($($T:ty),* $(,)?) => ($( impl_conversion_ref!($T => $T); )*); } +use std::borrow::Cow; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; +use std::num::{ + NonZeroIsize, NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, + NonZeroUsize, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, +}; + impl_from_uri_param_identity! { String, i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64, bool, - IpAddr, Ipv4Addr, Ipv6Addr + IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, + NonZeroIsize, NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, + NonZeroUsize, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, + time::Date, time::Time, time::PrimitiveDateTime, } impl_from_uri_param_identity! { @@ -380,3 +394,48 @@ impl> FromUriParam param.map(T::from_uri_param) } } + +macro_rules! impl_map_conversion { + ($From:ident => $To:ident) => ( + impl FromUriParam> for $To + where A: UriDisplay, K: FromUriParam, + B: UriDisplay, V: FromUriParam + { + type Target = $From; + + #[inline(always)] + fn from_uri_param(param: $From) -> Self::Target { + param + } + } + ); + + (& $([$mut:tt])? $From:ident => $To:ident) => ( + impl<'a, K, V, A, B> FromUriParam> for $To + where A: UriDisplay, K: FromUriParam, + B: UriDisplay, V: FromUriParam + { + type Target = &'a $From; + + #[inline(always)] + fn from_uri_param(param: &'a $($mut)? $From) -> Self::Target { + param + } + } + ); +} + +impl_map_conversion!(HashMap => HashMap); +impl_map_conversion!(HashMap => BTreeMap); +impl_map_conversion!(BTreeMap => BTreeMap); +impl_map_conversion!(BTreeMap => HashMap); + +impl_map_conversion!(&HashMap => HashMap); +impl_map_conversion!(&HashMap => BTreeMap); +impl_map_conversion!(&BTreeMap => BTreeMap); +impl_map_conversion!(&BTreeMap => HashMap); + +impl_map_conversion!(&[mut] HashMap => HashMap); +impl_map_conversion!(&[mut] HashMap => BTreeMap); +impl_map_conversion!(&[mut] BTreeMap => BTreeMap); +impl_map_conversion!(&[mut] BTreeMap => HashMap); diff --git a/core/http/src/uri/fmt/uri_display.rs b/core/http/src/uri/fmt/uri_display.rs index 8e27bd57..50ecf187 100644 --- a/core/http/src/uri/fmt/uri_display.rs +++ b/core/http/src/uri/fmt/uri_display.rs @@ -342,6 +342,7 @@ use std::num::{ NonZeroUsize, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, }; +// Keep in-sync with the 'FromUriParam' impls. impl_with_display! { i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, @@ -367,6 +368,7 @@ macro_rules! impl_with_string { use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; +// Keep in-sync with the 'FromUriParam' impls. impl_with_string! { time::Date => |d| d.format("%F"), time::PrimitiveDateTime => |d| d.format("%FT%T"), @@ -377,7 +379,7 @@ impl_with_string! { } // These are second level implementations: they all defer to an existing -// implementation. +// implementation. Keep in-sync with `FromUriParam` impls. /// Percent-encodes the raw string. Defers to `str`. impl UriDisplay

for String {