use std::pin::Pin;
use futures::Future;
use ::tide::http::StatusCode;
use ::uni_components::Kind;
#[doc(hidden)]
pub mod uni_components {
pub use uni_components::*;
}
#[doc(hidden)]
pub mod tide {
pub use tide::*;
}
#[doc(hidden)]
pub mod async_trait {
pub use async_trait::*;
}
pub use uni_localservice_macro::*;
#[doc(hidden)]
#[async_trait::async_trait]
pub trait LocalService {
type In: 'static + serde::de::DeserializeOwned + serde::Serialize;
type Out: 'static + uni_components::AsResponse;
type Env: 'static + serde::de::DeserializeOwned + serde::Serialize + Send + Sync;
type Service: uni_components::service::Service<In = Self::In, Out = Self::Out>;
fn service(&self) -> &'static Self::Service;
async fn exec(
&mut self,
data: Self::In,
env: &Self::Env,
) -> Self::Out;
async fn postprocess(
&mut self,
response: crate::tide::Response,
) -> tide::Response {
return response;
}
}
#[doc(hidden)]
pub struct Relay<I, O, E, S, T>
where
I: 'static + serde::de::DeserializeOwned + serde::Serialize + Send + Sync,
O: 'static + uni_components::AsResponse,
E: 'static + serde::de::DeserializeOwned + serde::Serialize + Send + Sync,
S: uni_components::service::Service<In = I, Out = O>,
T: LocalService<In = I, Out = O, Env = E, Service = S> + Clone,
{
pub ls: T,
}
#[doc(hidden)]
pub struct RelayNoEnv<I, O, S, T>
where
I: 'static + serde::de::DeserializeOwned + serde::Serialize + Send + Sync,
O: 'static + uni_components::AsResponse,
S: uni_components::service::Service<In = I, Out = O>,
T: LocalService<In = I, Out = O, Env = (), Service = S> + Clone,
{
pub ls: T,
}
#[doc(hidden)]
#[derive(thiserror::Error, Debug)]
enum PreprocessError {
#[error("Couldn't convert parameter body to data")]
BodyExtractError,
#[error("Couldn't convert query to data")]
SerdeQsError(#[from] serde_urlencoded::de::Error),
#[error("Query not found")]
QueryNotFound,
}
impl<I, O, S, LS, T: 'static + Send + Sync + Clone> tide::Endpoint<T>
for RelayNoEnv<I, O, S, LS>
where
I: 'static + serde::de::DeserializeOwned + serde::Serialize + Send + Sync,
O: 'static + uni_components::AsResponse,
S: 'static + uni_components::service::Service<In = I, Out = O>,
LS: 'static
+ LocalService<In = I, Out = O, Env = (), Service = S>
+ Clone
+ Send
+ Sync,
{
fn call<'a, 'b>(
&'a self,
mut req: tide::Request<T>,
) -> Pin<Box<dyn Future<Output = tide::Result> + Send + 'b>>
where
'a: 'b,
Self: 'b,
{
let mut ls = self.ls.clone();
return Box::pin(async move {
let kind = ls.service().kind();
let data = match kind {
Kind::Get => {
match req.url().query() {
Some(query) => {
serde_urlencoded::from_str::<I>(query)
.map_err(|e| PreprocessError::SerdeQsError(e))
},
None => Err(PreprocessError::QueryNotFound),
}
},
Kind::Post => {
req.body_json::<I>().await.map_err(|e| {
log::warn!("Request body extraction error:{:?}", e);
PreprocessError::BodyExtractError
})
},
};
return match data {
Ok(data) => {
let response = ls.exec(data, &()).await.to_response();
let mut result = tide::Response::new(response.code);
if let Some(body) = response.body {
result.set_body(body);
}
let result = ls.postprocess(result).await;
Ok(result)
},
Err(e) => {
log::error!(
"GenericLocal: Couldn't extract data from body e:{:?}",
e
);
Ok(tide::Response::new(StatusCode::BadRequest))
},
};
});
}
}
impl<I, O, E, S, LS, T: 'static + Send + Sync + Clone> tide::Endpoint<T>
for Relay<I, O, E, S, LS>
where
I: 'static + serde::de::DeserializeOwned + serde::Serialize + Send + Sync,
O: 'static + uni_components::AsResponse,
E: 'static + serde::de::DeserializeOwned + serde::Serialize + Send + Sync,
S: 'static + uni_components::service::Service<In = I, Out = O>,
LS: 'static
+ LocalService<In = I, Out = O, Env = E, Service = S>
+ Clone
+ Send
+ Sync,
{
fn call<'a, 'b>(
&'a self,
mut req: tide::Request<T>,
) -> Pin<Box<dyn Future<Output = tide::Result> + Send + 'b>>
where
'a: 'b,
Self: 'b,
{
let mut ls = self.ls.clone();
return Box::pin(async move {
let kind = ls.service().kind();
let data = match kind {
Kind::Get => {
match req.url().query() {
Some(query) => {
serde_urlencoded::from_str::<I>(query)
.map_err(|e| PreprocessError::SerdeQsError(e))
},
None => Err(PreprocessError::QueryNotFound),
}
},
Kind::Post => {
req.body_json::<I>().await.map_err(|e| {
log::warn!("Request body extraction error:{:?}", e);
PreprocessError::BodyExtractError
})
},
};
let env = req.ext();
return match (data, env) {
(Ok(data), Some(env)) => {
let response = ls.exec(data, env).await.to_response();
let mut result = tide::Response::new(response.code);
if let Some(body) = response.body {
result.set_body(body);
}
let result = ls.postprocess(result).await;
Ok(result)
},
(Err(e), _) => {
log::error!(
"GenericLocal: Couldn't extract data from body e:{:?}",
e
);
Ok(tide::Response::new(StatusCode::BadRequest))
},
(_, None) => {
log::error!(
"Couldn't extract {} from local state",
std::any::type_name::<E>()
);
Ok(tide::Response::new(StatusCode::InternalServerError))
},
};
});
}
}