use std::collections::{BTreeMap, HashMap};
use std::{fmt, path};
use std::borrow::Cow;
use time::{macros::format_description, format_description::FormatItem};
use crate::RawStr;
use crate::uri::fmt::{Part, Path, Query, Formatter};
pub trait UriDisplay<P: Part> {
fn fmt(&self, f: &mut Formatter<'_, P>) -> fmt::Result;
}
impl<P: Part> fmt::Display for &dyn UriDisplay<P> {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
UriDisplay::fmt(*self, &mut <Formatter<'_, P>>::new(f))
}
}
impl<P: Part> UriDisplay<P> for str {
#[inline(always)]
fn fmt(&self, f: &mut Formatter<'_, P>) -> fmt::Result {
f.write_raw(RawStr::new(self).percent_encode().as_str())
}
}
impl UriDisplay<Path> for path::Path {
fn fmt(&self, f: &mut Formatter<'_, Path>) -> fmt::Result {
use std::path::Component;
for component in self.components() {
match component {
Component::Prefix(_) | Component::RootDir => continue,
_ => f.write_value(&component.as_os_str().to_string_lossy())?
}
}
Ok(())
}
}
macro_rules! impl_with_display {
($($T:ty),+ $(,)?) => {$(
impl<P: Part> UriDisplay<P> for $T {
#[inline(always)]
fn fmt(&self, f: &mut Formatter<'_, P>) -> fmt::Result {
use std::fmt::Write;
write!(f, "{}", self)
}
}
)+}
}
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,
NonZeroIsize, NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128,
NonZeroUsize, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128,
}
macro_rules! impl_with_string {
($($T:ty => $f:expr),+ $(,)?) => {$(
impl<P: Part> UriDisplay<P> for $T {
#[inline(always)]
fn fmt(&self, f: &mut Formatter<'_, P>) -> fmt::Result {
let func: fn(&$T) -> Result<String, fmt::Error> = $f;
func(self).and_then(|s| s.as_str().fmt(f))
}
}
)+}
}
use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
static DATE_FMT: &[FormatItem<'_>] = format_description!("[year padding:none]-[month]-[day]");
static TIME_FMT: &[FormatItem<'_>] = format_description!("[hour padding:none]:[minute]:[second]");
static DATE_TIME_FMT: &[FormatItem<'_>] =
format_description!("[year padding:none]-[month]-[day]T[hour padding:none]:[minute]:[second]");
impl_with_string! {
time::Date => |d| d.format(&DATE_FMT).map_err(|_| fmt::Error),
time::Time => |d| d.format(&TIME_FMT).map_err(|_| fmt::Error),
time::PrimitiveDateTime => |d| d.format(&DATE_TIME_FMT).map_err(|_| fmt::Error),
SocketAddr => |a| Ok(a.to_string()),
SocketAddrV4 => |a| Ok(a.to_string()),
SocketAddrV6 => |a| Ok(a.to_string()),
}
impl<P: Part> UriDisplay<P> for String {
#[inline(always)]
fn fmt(&self, f: &mut Formatter<'_, P>) -> fmt::Result {
self.as_str().fmt(f)
}
}
impl<P: Part> UriDisplay<P> for Cow<'_, str> {
#[inline(always)]
fn fmt(&self, f: &mut Formatter<'_, P>) -> fmt::Result {
self.as_ref().fmt(f)
}
}
impl UriDisplay<Path> for path::PathBuf {
#[inline(always)]
fn fmt(&self, f: &mut Formatter<'_, Path>) -> fmt::Result {
self.as_path().fmt(f)
}
}
impl<P: Part, T: UriDisplay<P> + ?Sized> UriDisplay<P> for &T {
#[inline(always)]
fn fmt(&self, f: &mut Formatter<'_, P>) -> fmt::Result {
UriDisplay::fmt(*self, f)
}
}
impl<P: Part, T: UriDisplay<P> + ?Sized> UriDisplay<P> for &mut T {
#[inline(always)]
fn fmt(&self, f: &mut Formatter<'_, P>) -> fmt::Result {
UriDisplay::fmt(*self, f)
}
}
impl<T: UriDisplay<Query>> UriDisplay<Query> for Option<T> {
#[inline(always)]
fn fmt(&self, f: &mut Formatter<'_, Query>) -> fmt::Result {
match self {
Some(v) => v.fmt(f),
None => Ok(())
}
}
}
impl<T: UriDisplay<Query>, E> UriDisplay<Query> for Result<T, E> {
#[inline(always)]
fn fmt(&self, f: &mut Formatter<'_, Query>) -> fmt::Result {
match self {
Ok(v) => v.fmt(f),
Err(_) => Ok(())
}
}
}
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)?;
}
Ok(())
}
}
impl<K: UriDisplay<Query>, V: UriDisplay<Query>> UriDisplay<Query> for HashMap<K, V> {
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<K: UriDisplay<Query>, V: UriDisplay<Query>> UriDisplay<Query> for BTreeMap<K, V> {
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);
pub trait Ignorable<P: Part> { }
impl<T> Ignorable<Query> for Option<T> { }
impl<T, E> Ignorable<Query> for Result<T, E> { }
#[doc(hidden)]
pub fn assert_ignorable<P: Part, T: Ignorable<P>>() { }
#[cfg(test)]
mod uri_display_tests {
use std::path;
use crate::uri::fmt::{FromUriParam, UriDisplay};
use crate::uri::fmt::{Query, Path};
macro_rules! uri_display {
(<$P:ident, $Target:ty> $source:expr) => ({
let tmp = $source;
let target = <$Target as FromUriParam<$P, _>>::from_uri_param(tmp);
format!("{}", &target as &dyn UriDisplay<$P>)
})
}
macro_rules! assert_display {
(<$P:ident, $Target:ty> $source:expr, $expected:expr) => ({
assert_eq!(uri_display!(<$P, $Target> $source), $expected);
})
}
#[test]
fn uri_display_encoding() {
assert_display!(<Query, String> "hello", "hello");
assert_display!(<Query, String> "hi hi", "hi%20hi");
assert_display!(<Query, &str> "hi hi", "hi%20hi");
assert_display!(<Query, &str> &"hi hi", "hi%20hi");
assert_display!(<Query, usize> 10, "10");
assert_display!(<Query, u8> 10, "10");
assert_display!(<Query, i32> 10, "10");
assert_display!(<Query, isize> 10, "10");
assert_display!(<Path, String> "hello", "hello");
assert_display!(<Path, String> "hi hi", "hi%20hi");
assert_display!(<Path, &str> "hi hi", "hi%20hi");
assert_display!(<Path, &str> &"hi hi", "hi%20hi");
assert_display!(<Path, usize> 10, "10");
assert_display!(<Path, u8> 10, "10");
assert_display!(<Path, i32> 10, "10");
assert_display!(<Path, isize> 10, "10");
assert_display!(<Query, &str> &"hi there", "hi%20there");
assert_display!(<Query, isize> &10, "10");
assert_display!(<Query, u8> &10, "10");
assert_display!(<Path, &str> &"hi there", "hi%20there");
assert_display!(<Path, isize> &10, "10");
assert_display!(<Path, u8> &10, "10");
assert_display!(<Path, Option<&str>> &"hi there", "hi%20there");
assert_display!(<Path, Option<isize>> &10, "10");
assert_display!(<Path, Option<u8>> &10, "10");
assert_display!(<Query, Option<&str>> Some(&"hi there"), "hi%20there");
assert_display!(<Query, Option<isize>> Some(&10), "10");
assert_display!(<Query, Option<u8>> Some(&10), "10");
assert_display!(<Path, Result<&str, usize>> &"hi there", "hi%20there");
assert_display!(<Path, Result<isize, &str>> &10, "10");
assert_display!(<Path, Result<u8, String>> &10, "10");
assert_display!(<Query, Result<&str, usize>> Ok(&"hi there"), "hi%20there");
assert_display!(<Query, Result<isize, &str>> Ok(&10), "10");
assert_display!(<Query, Result<u8, String>> Ok(&10), "10");
}
#[test]
fn paths() {
assert_display!(<Path, path::PathBuf> "hello", "hello");
assert_display!(<Path, path::PathBuf> "hi there", "hi%20there");
assert_display!(<Path, path::PathBuf> "hello/world", "hello/world");
assert_display!(<Path, path::PathBuf> "hello//world", "hello/world");
assert_display!(<Path, path::PathBuf> "hello/ world", "hello/%20world");
assert_display!(<Path, path::PathBuf> "hi/wo rld", "hi/wo%20rld");
assert_display!(<Path, path::PathBuf> &"hi/wo rld", "hi/wo%20rld");
assert_display!(<Path, path::PathBuf> &"hi there", "hi%20there");
}
struct Wrapper<T>(T);
impl<A, T: FromUriParam<Query, A>> FromUriParam<Query, A> for Wrapper<T> {
type Target = T::Target;
#[inline(always)]
fn from_uri_param(param: A) -> Self::Target {
T::from_uri_param(param)
}
}
impl FromUriParam<Path, usize> for Wrapper<usize> {
type Target = usize;
#[inline(always)]
fn from_uri_param(param: usize) -> Self::Target {
param
}
}
#[test]
fn uri_display_encoding_wrapped() {
assert_display!(<Query, Option<Wrapper<&str>>> Some(&"hi there"), "hi%20there");
assert_display!(<Query, Option<Wrapper<&str>>> Some("hi there"), "hi%20there");
assert_display!(<Query, Option<Wrapper<isize>>> Some(10), "10");
assert_display!(<Query, Option<Wrapper<usize>>> Some(18), "18");
assert_display!(<Path, Option<Wrapper<usize>>> 238, "238");
assert_display!(<Path, Result<Option<Wrapper<usize>>, usize>> 238, "238");
assert_display!(<Path, Option<Result<Wrapper<usize>, usize>>> 123, "123");
}
#[test]
fn check_ignorables() {
use crate::uri::fmt::assert_ignorable;
assert_ignorable::<Query, Option<usize>>();
assert_ignorable::<Query, Option<Wrapper<usize>>>();
assert_ignorable::<Query, Result<Wrapper<usize>, usize>>();
assert_ignorable::<Query, Option<Result<Wrapper<usize>, usize>>>();
assert_ignorable::<Query, Result<Option<Wrapper<usize>>, usize>>();
}
}