use std::{borrow::Cow, sync::Arc};
use crate::{MakeRequest, Operation, Request};
pub trait Server {
fn base_url(&self) -> Cow<'_, str>;
}
impl Server for str {
fn base_url(&self) -> Cow<'_, str> {
Cow::Borrowed(self)
}
}
impl Server for String {
fn base_url(&self) -> Cow<'_, str> {
Cow::Borrowed(self.as_str())
}
}
impl Server for Arc<str> {
fn base_url(&self) -> Cow<'_, str> {
Cow::Borrowed(self.as_ref())
}
}
impl<T> Server for &T
where
T: Server + ?Sized,
{
fn base_url(&self) -> Cow<'_, str> {
(**self).base_url()
}
}
impl<T> Server for Box<T>
where
T: Server + ?Sized,
{
fn base_url(&self) -> Cow<'_, str> {
(**self).base_url()
}
}
pub struct WithServer<Op> {
op: Op,
base_url: Arc<str>,
}
impl<Op> WithServer<Op> {
pub fn new<Srv: Server>(op: Op, server: Srv) -> Self {
Self {
op,
base_url: Arc::from(server.base_url().as_ref()),
}
}
pub fn into_parts(self) -> (Op, Arc<str>) {
(self.op, self.base_url)
}
}
impl<Op> MakeRequest for WithServer<Op>
where
Op: MakeRequest + Send,
{
type Error = Op::Error;
#[allow(clippy::manual_async_fn)]
fn make_request(
self,
) -> impl std::future::Future<Output = Result<Request, Self::Error>> + Send {
async move {
let req = self.op.make_request().await?;
Ok(rewrite_base_url(req, self.base_url.as_ref()))
}
}
}
impl<Op> Operation for WithServer<Op>
where
Op: Operation + Send,
{
type Response = Op::Response;
}
pub(crate) fn rewrite_base_url(req: Request, base_url: &str) -> Request {
let (mut parts, body) = req.into_parts();
let path_and_query = parts
.uri
.path_and_query()
.map(ToString::to_string)
.unwrap_or_default();
let combined = format!("{}{}", trim_trailing_slash(base_url), path_and_query);
if let Ok(new_uri) = combined.parse() {
parts.uri = new_uri;
}
Request::from_parts(parts, body)
}
pub(crate) fn prefix_base_url(req: Request, base_url: &str) -> Request {
if req.uri().scheme().is_some() {
return req;
}
rewrite_base_url(req, base_url)
}
fn trim_trailing_slash(s: &str) -> &str {
s.strip_suffix('/').unwrap_or(s)
}