use std::fmt::{self, Write};
use std::marker::PhantomData;
use std::borrow::Cow;
use smallvec::SmallVec;
use crate::uri::{Absolute, Origin, Reference};
use crate::uri::fmt::{UriDisplay, Part, Path, Query, Kind};
pub struct Formatter<'i, P: Part> {
prefixes: SmallVec<[&'static str; 3]>,
inner: &'i mut (dyn Write + 'i),
previous: bool,
fresh: bool,
_marker: PhantomData<P>,
}
impl<'i, P: Part> Formatter<'i, P> {
#[inline(always)]
pub(crate) fn new(inner: &'i mut (dyn Write + 'i)) -> Self {
Formatter {
inner,
prefixes: SmallVec::new(),
previous: false,
fresh: true,
_marker: PhantomData,
}
}
#[inline(always)]
fn refreshed<F: FnOnce(&mut Self) -> fmt::Result>(&mut self, f: F) -> fmt::Result {
self.refresh();
let result = f(self);
self.refresh();
result
}
pub fn write_raw<S: AsRef<str>>(&mut self, string: S) -> fmt::Result {
if self.fresh {
if self.previous {
self.inner.write_char(P::DELIMITER)?;
}
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)?;
}
self.inner.write_str("=")?;
}
}
self.fresh = false;
self.previous = true;
self.inner.write_str(string.as_ref())
}
#[inline]
pub fn write_value<T: UriDisplay<P>>(&mut self, value: T) -> fmt::Result {
self.refreshed(|f| UriDisplay::fmt(&value, f))
}
#[inline(always)]
pub fn refresh(&mut self) {
self.fresh = true;
}
}
impl Formatter<'_, Query> {
fn with_prefix<F>(&mut self, prefix: &str, f: F) -> fmt::Result
where F: FnOnce(&mut Self) -> fmt::Result
{
struct PrefixGuard<'f, 'i>(&'f mut Formatter<'i, Query>);
impl<'f, 'i> PrefixGuard<'f, 'i> {
fn new(prefix: &str, f: &'f mut Formatter<'i, Query>) -> Self {
let prefix = unsafe { std::mem::transmute(prefix) };
f.prefixes.push(prefix);
PrefixGuard(f)
}
}
impl Drop for PrefixGuard<'_, '_> {
fn drop(&mut self) {
self.0.prefixes.pop();
}
}
f(&mut PrefixGuard::new(prefix, self).0)
}
#[inline]
pub fn write_named_value<T: UriDisplay<Query>>(&mut self, name: &str, value: T) -> fmt::Result {
self.refreshed(|f| f.with_prefix(name, |f| f.write_value(value)))
}
}
impl<P: Part> fmt::Write for Formatter<'_, P> {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.write_raw(s)
}
}
#[doc(hidden)]
pub enum UriArgumentsKind<A> {
Static(&'static str),
Dynamic(A)
}
#[doc(hidden)]
pub enum UriQueryArgument<'a> {
Raw(&'a str),
NameValue(&'a str, &'a dyn UriDisplay<Query>),
Value(&'a dyn UriDisplay<Query>)
}
#[doc(hidden)]
pub struct Void;
#[doc(hidden)]
pub trait ValidRoutePrefix {
type Output;
fn append(self, path: Cow<'static, str>, query: Option<Cow<'static, str>>) -> Self::Output;
}
impl<'a> ValidRoutePrefix for Origin<'a> {
type Output = Self;
fn append(self, path: Cow<'static, str>, query: Option<Cow<'static, str>>) -> Self::Output {
let mut prefix = self.into_normalized();
prefix.clear_query();
if prefix.path() == "/" {
return Origin::new(path, query);
} else if path == "/" {
prefix.set_query(query);
return prefix;
}
Origin::new(format!("{}{}", prefix.path(), path), query)
}
}
impl<'a> ValidRoutePrefix for Absolute<'a> {
type Output = Self;
fn append(self, path: Cow<'static, str>, query: Option<Cow<'static, str>>) -> Self::Output {
let mut prefix = self.into_normalized();
prefix.clear_query();
if prefix.authority().is_some() {
if path == "/" {
prefix.set_query(query);
return prefix;
}
}
if prefix.path().is_empty() || prefix.path() == "/" {
prefix.set_path(path);
prefix.set_query(query);
return prefix;
}
if path == "/" {
prefix.set_query(query);
return prefix;
}
prefix.set_path(format!("{}{}", prefix.path(), path));
prefix.set_query(query);
prefix
}
}
#[doc(hidden)]
pub trait ValidRouteSuffix<T> {
type Output;
fn prepend(self, prefix: T) -> Self::Output;
}
impl<'a> ValidRouteSuffix<Origin<'a>> for Reference<'a> {
type Output = Self;
fn prepend(self, prefix: Origin<'a>) -> Self::Output {
Reference::from(prefix).with_query_fragment_of(self)
}
}
impl<'a> ValidRouteSuffix<Absolute<'a>> for Reference<'a> {
type Output = Self;
fn prepend(self, prefix: Absolute<'a>) -> Self::Output {
Reference::from(prefix).with_query_fragment_of(self)
}
}
impl<'a> ValidRouteSuffix<Origin<'a>> for Absolute<'a> {
type Output = Origin<'a>;
fn prepend(self, mut prefix: Origin<'a>) -> Self::Output {
if let Some(query) = self.query {
if prefix.query().is_none() {
prefix.set_query(query.value.into_concrete(&self.source));
}
}
prefix
}
}
impl<'a> ValidRouteSuffix<Absolute<'a>> for Absolute<'a> {
type Output = Self;
fn prepend(self, mut prefix: Absolute<'a>) -> Self::Output {
if let Some(query) = self.query {
if prefix.query().is_none() {
prefix.set_query(query.value.into_concrete(&self.source));
}
}
prefix
}
}
#[doc(hidden)]
pub struct RouteUriBuilder {
pub path: Cow<'static, str>,
pub query: Option<Cow<'static, str>>,
}
#[doc(hidden)]
pub struct PrefixedRouteUri<T>(T);
#[doc(hidden)]
pub struct SuffixedRouteUri<T>(T);
#[doc(hidden)]
impl RouteUriBuilder {
pub fn new(
path_args: UriArgumentsKind<&[&dyn UriDisplay<Path>]>,
query_args: Option<UriArgumentsKind<&[UriQueryArgument<'_>]>>,
) -> Self {
use self::{UriArgumentsKind::*, UriQueryArgument::*};
let path: Cow<'static, str> = match path_args {
Static(path) => path.into(),
Dynamic(args) => {
let mut string = String::from("/");
let mut formatter = Formatter::<Path>::new(&mut string);
for value in args {
let _ = formatter.write_value(value);
}
string.into()
}
};
let query: Option<Cow<'_, str>> = match query_args {
None => None,
Some(Static(query)) => Some(query.into()),
Some(Dynamic(args)) => {
let mut string = String::new();
let mut f = Formatter::<Query>::new(&mut string);
for arg in args {
let _ = match arg {
Raw(v) => f.write_raw(v),
NameValue(n, v) => f.write_named_value(n, v),
Value(v) => f.write_value(v),
};
}
(!string.is_empty()).then(|| string.into())
}
};
RouteUriBuilder { path, query }
}
pub fn with_prefix<P: ValidRoutePrefix>(self, p: P) -> PrefixedRouteUri<P::Output> {
PrefixedRouteUri(p.append(self.path, self.query))
}
pub fn with_suffix<S>(self, suffix: S) -> SuffixedRouteUri<S::Output>
where S: ValidRouteSuffix<Origin<'static>>
{
SuffixedRouteUri(suffix.prepend(self.render()))
}
pub fn render(self) -> Origin<'static> {
Origin::new(self.path, self.query)
}
}
#[doc(hidden)]
impl<T> PrefixedRouteUri<T> {
pub fn with_suffix<S: ValidRouteSuffix<T>>(self, suffix: S) -> SuffixedRouteUri<S::Output> {
SuffixedRouteUri(suffix.prepend(self.0))
}
pub fn render(self) -> T {
self.0
}
}
#[doc(hidden)]
impl<T> SuffixedRouteUri<T> {
pub fn render(self) -> T {
self.0
}
}
#[cfg(test)]
mod prefix_soundness_test {
use crate::uri::fmt::{Formatter, UriDisplay, Query};
struct MyValue;
impl UriDisplay<Query> for MyValue {
fn fmt(&self, _f: &mut Formatter<'_, Query>) -> std::fmt::Result {
panic!()
}
}
struct MyDisplay;
impl UriDisplay<Query> for MyDisplay {
fn fmt(&self, formatter: &mut Formatter<'_, Query>) -> std::fmt::Result {
struct Wrapper<'a, 'b>(&'a mut Formatter<'b, Query>);
impl<'a, 'b> Drop for Wrapper<'a, 'b> {
fn drop(&mut self) {
let _overlap = String::from("12345");
self.0.write_raw("world").ok();
assert!(self.0.prefixes.is_empty());
}
}
let wrapper = Wrapper(formatter);
let temporary_string = String::from("hello");
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
wrapper.0.write_named_value(&temporary_string, MyValue)
}));
Ok(())
}
}
#[test]
fn check_consistency() {
let string = format!("{}", &MyDisplay as &dyn UriDisplay<Query>);
assert_eq!(string, "world");
}
}