diff --git a/core/http/src/uri/fmt/formatter.rs b/core/http/src/uri/fmt/formatter.rs index 1622279f..319f850d 100644 --- a/core/http/src/uri/fmt/formatter.rs +++ b/core/http/src/uri/fmt/formatter.rs @@ -215,10 +215,8 @@ impl<'i, P: Part> Formatter<'i, P> { if P::KIND == Kind::Query && !self.prefixes.is_empty() { for (i, prefix) in self.prefixes.iter().enumerate() { + if i != 0 { self.inner.write_char('.')? } self.inner.write_str(prefix)?; - if i < self.prefixes.len() - 1 { - self.inner.write_str(".")?; - } } self.inner.write_str("=")?; diff --git a/core/http/src/uri/fmt/uri_display.rs b/core/http/src/uri/fmt/uri_display.rs index 2673d5ff..8e27bd57 100644 --- a/core/http/src/uri/fmt/uri_display.rs +++ b/core/http/src/uri/fmt/uri_display.rs @@ -1,3 +1,4 @@ +use std::collections::{BTreeMap, HashMap}; use std::{fmt, path}; use std::borrow::Cow; @@ -323,7 +324,7 @@ impl UriDisplay for path::Path { } macro_rules! impl_with_display { - ($($T:ty),+) => {$( + ($($T:ty),+ $(,)?) => {$( /// This implementation is identical to the `Display` implementation. impl UriDisplay

for $T { #[inline(always)] @@ -336,12 +337,43 @@ macro_rules! impl_with_display { } use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::num::{ + NonZeroIsize, NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, + NonZeroUsize, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, +}; impl_with_display! { i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64, bool, - IpAddr, Ipv4Addr, Ipv6Addr + IpAddr, Ipv4Addr, Ipv6Addr, + NonZeroIsize, NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, + NonZeroUsize, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, +} + +macro_rules! impl_with_string { + ($($T:ty => $f:expr),+ $(,)?) => {$( + /// This implementation is identical to a percent-encoded versiono the + /// `Display` implementation. + impl UriDisplay

for $T { + #[inline(always)] + fn fmt(&self, f: &mut Formatter<'_, P>) -> fmt::Result { + let func: fn(&$T) -> String = $f; + func(self).as_str().fmt(f) + } + } + )+} +} + +use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; + +impl_with_string! { + time::Date => |d| d.format("%F"), + time::PrimitiveDateTime => |d| d.format("%FT%T"), + time::Time => |d| d.format("%T"), + SocketAddr => |s| s.to_string(), + SocketAddrV4 => |s| s.to_string(), + SocketAddrV6 => |s| s.to_string(), } // These are second level implementations: they all defer to an existing @@ -409,6 +441,52 @@ impl, E> UriDisplay for Result { } } +impl> UriDisplay for Vec { + fn fmt(&self, f: &mut Formatter<'_, Query>) -> fmt::Result { + for value in self { + f.write_value(value)?; + } + + Ok(()) + } +} + +impl, V: UriDisplay> UriDisplay for HashMap { + fn fmt(&self, f: &mut Formatter<'_, Query>) -> fmt::Result { + use std::fmt::Write; + + let mut field_name = String::with_capacity(8); + for (i, (key, value)) in self.iter().enumerate() { + field_name.truncate(0); + write!(field_name, "k:{}", i)?; + f.write_named_value(&field_name, key)?; + + field_name.replace_range(..1, "v"); + f.write_named_value(&field_name, value)?; + } + + Ok(()) + } +} + +impl, V: UriDisplay> UriDisplay for BTreeMap { + fn fmt(&self, f: &mut Formatter<'_, Query>) -> fmt::Result { + use std::fmt::Write; + + let mut field_name = String::with_capacity(8); + for (i, (key, value)) in self.iter().enumerate() { + field_name.truncate(0); + write!(field_name, "k:{}", i)?; + f.write_named_value(&field_name, key)?; + + field_name.replace_range(..1, "v"); + f.write_named_value(&field_name, value)?; + } + + Ok(()) + } +} + #[cfg(feature = "uuid")] impl_with_display!(uuid_::Uuid); #[cfg(feature = "uuid")] crate::impl_from_uri_param_identity!(uuid_::Uuid);