use std::borrow::Cow;
use faststr::FastStr;
use http::{header, uri::Uri};
use motore::{layer::Layer, service::Service};
use volo::{client::Apply, context::Context, net::Address};
use crate::{
client::Target,
context::ClientContext,
error::{ClientError, client::Result},
request::Request,
};
pub struct TargetLayer {
target: Target,
service_name: FastStr,
}
impl TargetLayer {
pub const fn new(target: Target) -> Self {
Self {
target,
service_name: FastStr::empty(),
}
}
pub fn new_address<A>(addr: A) -> Self
where
A: Into<Address>,
{
let addr = addr.into();
let target = Target::from(addr);
Self {
target,
service_name: FastStr::empty(),
}
}
pub fn new_host<S>(host: S) -> Self
where
S: Into<Cow<'static, str>>,
{
let target = Target::from_host(host);
Self {
target,
service_name: FastStr::empty(),
}
}
pub fn from_uri(uri: &Uri) -> Self {
let target = Target::from_uri(uri).expect("invalid uri for building target");
Self {
target,
service_name: FastStr::empty(),
}
}
pub fn with_service_name<S>(mut self, service_name: S) -> Self
where
S: Into<FastStr>,
{
self.service_name = service_name.into();
self
}
}
impl<S> Layer<S> for TargetLayer {
type Service = TargetService<S>;
fn layer(self, inner: S) -> Self::Service {
TargetService {
inner,
target: self.target,
service_name: self.service_name,
}
}
}
pub struct TargetService<S> {
inner: S,
target: Target,
service_name: FastStr,
}
impl<B, S> Service<ClientContext, Request<B>> for TargetService<S>
where
B: Send,
S: Service<ClientContext, Request<B>, Error = ClientError> + Send + Sync,
{
type Response = S::Response;
type Error = S::Error;
async fn call(
&self,
cx: &mut ClientContext,
mut req: Request<B>,
) -> Result<Self::Response, Self::Error> {
self.target.clone().apply(cx)?;
if !self.service_name.is_empty() {
cx.rpc_info_mut()
.callee_mut()
.set_service_name(self.service_name.clone());
}
if !req.headers().contains_key(header::HOST) {
if let Some(host) = super::header::gen_host(&self.target) {
req.headers_mut().insert(header::HOST, host);
}
}
self.inner.call(cx, req).await
}
}