#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
use salvo::http::HeaderValue;
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
use salvo::hyper::header::CONTENT_TYPE;
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
use salvo::oapi::{EndpointOutRegister, ToSchema};
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
use salvo::{Depot, Request, Response};
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
use salvo::Writer as ServerResponseWriter;
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
use salvo::fs::NamedFile;
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[macro_export]
macro_rules! fn_name {
() => {{
fn f() {}
fn type_name_of<T>(_: T) -> &'static str {
std::any::type_name::<T>()
}
let name = type_name_of(f);
match name[..name.len() - 3].rsplit("::").nth(2) {
Some(el) => el,
None => &name[..name.len() - 3],
}
}};
}
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
macro_rules! impl_oapi_endpoint_out {
($t:tt, $c:expr) => {
#[cfg(feature = "salvo")]
impl EndpointOutRegister for $t {
#[inline]
fn register(components: &mut salvo::oapi::Components, operation: &mut salvo::oapi::Operation) {
operation.responses.insert(
"200",
salvo::oapi::Response::new("Ok").add_content($c, String::to_schema(components)),
);
}
}
};
}
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
macro_rules! impl_oapi_endpoint_out_t {
($t:tt, $c:expr) => {
#[cfg(feature = "salvo")]
impl<T> EndpointOutRegister for $t<T> {
#[inline]
fn register(components: &mut salvo::oapi::Components, operation: &mut salvo::oapi::Operation) {
operation.responses.insert(
"200",
salvo::oapi::Response::new("Ok").add_content($c, String::to_schema(components)),
);
}
}
};
}
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[allow(async_fn_in_trait)]
pub trait ExplicitServerWrite {
async fn explicit_write(self, res: &mut Response);
}
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
pub struct OK(pub &'static str);
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
impl_oapi_endpoint_out!(OK, "text/plain");
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[macro_export]
macro_rules! ok {
() => {
Ok::<impulse_utils::responses::OK, impulse_utils::errors::ServerError>(impulse_utils::responses::OK(
$crate::fn_name!(),
))
};
}
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
impl ExplicitServerWrite for OK {
async fn explicit_write(self, res: &mut Response) {
res.status_code(salvo::http::StatusCode::OK);
res.render("");
tracing::trace!("[{}] => Received and sent result 200", self.0);
}
}
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[salvo::async_trait]
impl ServerResponseWriter for OK {
async fn write(self, _req: &mut Request, _depot: &mut Depot, res: &mut Response) {
ExplicitServerWrite::explicit_write(self, res).await
}
}
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[derive(Debug)]
pub struct Plain(pub String, pub &'static str);
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
impl_oapi_endpoint_out!(Plain, "text/plain");
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[macro_export]
macro_rules! plain {
($plain_text:expr) => {
Ok::<impulse_utils::responses::Plain, impulse_utils::errors::ServerError>(impulse_utils::responses::Plain(
$plain_text,
$crate::fn_name!(),
))
};
}
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
impl ExplicitServerWrite for Plain {
async fn explicit_write(self, res: &mut Response) {
res.status_code(salvo::http::StatusCode::OK);
res.render(&self.0);
tracing::trace!("[{}] => Received and sent result 200 with text: {}", self.1, self.0);
}
}
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[salvo::async_trait]
impl ServerResponseWriter for Plain {
async fn write(self, _req: &mut Request, _depot: &mut Depot, res: &mut Response) {
ExplicitServerWrite::explicit_write(self, res).await
}
}
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[derive(Debug)]
pub struct Html(pub String, pub &'static str);
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
impl_oapi_endpoint_out!(Html, "text/html");
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[macro_export]
macro_rules! html {
($html_data:expr) => {
Ok::<impulse_utils::responses::Html, impulse_utils::errors::ServerError>(impulse_utils::responses::Html(
$html_data,
$crate::fn_name!(),
))
};
}
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
impl ExplicitServerWrite for Html {
async fn explicit_write(self, res: &mut Response) {
res.status_code(salvo::http::StatusCode::OK);
res.render(salvo::writing::Text::Html(&self.0));
tracing::trace!("[{}] => Received and sent result 200 with HTML", self.1);
}
}
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[salvo::async_trait]
impl ServerResponseWriter for Html {
async fn write(self, _req: &mut Request, _depot: &mut Depot, res: &mut Response) {
ExplicitServerWrite::explicit_write(self, res).await
}
}
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[derive(Debug)]
pub struct File(pub std::path::PathBuf, pub String, pub &'static str);
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
impl_oapi_endpoint_out!(File, "application/octet-stream");
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[macro_export]
macro_rules! file_upload {
($filepath:expr, $attached_filename:expr) => {
Ok::<impulse_utils::responses::File, impulse_utils::errors::ServerError>(impulse_utils::responses::File(
$filepath,
$attached_filename,
$crate::fn_name!(),
))
};
}
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[salvo::async_trait]
impl ServerResponseWriter for File {
async fn write(self, req: &mut Request, _depot: &mut Depot, res: &mut Response) {
res.status_code(salvo::http::StatusCode::OK);
res.headers_mut().append(
"Cache-Control",
HeaderValue::from_static("public, max-age=0, must-revalidate"),
);
NamedFile::builder(&self.0)
.attached_name(&self.1)
.use_etag(true)
.use_last_modified(true)
.send(req.headers(), res)
.await;
tracing::trace!("[{}] => Received and sent result 200 with file {}", self.2, self.1);
}
}
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[derive(Debug)]
pub struct Json<T>(pub T, pub &'static str);
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
impl_oapi_endpoint_out_t!(Json, "application/json");
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[macro_export]
macro_rules! json {
($json_data:expr) => {
Ok::<impulse_utils::responses::Json<_>, impulse_utils::errors::ServerError>(impulse_utils::responses::Json(
$json_data,
$crate::fn_name!(),
))
};
}
#[cfg(all(feature = "salvo", feature = "mresult"))]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
impl<T: serde::Serialize + Send> ExplicitServerWrite for Json<T> {
async fn explicit_write(self, res: &mut Response) {
res.status_code(salvo::http::StatusCode::OK);
match sonic_rs::to_string(&self.0) {
Ok(s) => {
res.headers_mut().insert(
CONTENT_TYPE,
HeaderValue::from_static("application/json; charset=utf-8"),
);
tracing::trace!("[{}] => Sending JSON: {:?}", self.1, s.as_str());
res.write_body(s).ok();
tracing::trace!("[{}] => Received and sent result 200 with JSON", self.1);
}
Err(e) => {
tracing::error!("[{}] => Failed to serialize data: {:?}", e, self.1);
crate::prelude::ServerError::from_private(e)
.with_public("Failed to serialize data.")
.with_500()
.explicit_write(res)
.await;
}
}
}
}
#[cfg(all(feature = "salvo", feature = "mresult"))]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[salvo::async_trait]
impl<T: serde::Serialize + Send> ServerResponseWriter for Json<T> {
async fn write(self, _req: &mut Request, _depot: &mut Depot, res: &mut Response) {
ExplicitServerWrite::explicit_write(self, res).await
}
}
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[derive(Debug)]
pub struct MsgPack<T>(pub T, pub &'static str);
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
impl_oapi_endpoint_out_t!(MsgPack, "application/msgpack");
#[cfg(feature = "salvo")]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[macro_export]
macro_rules! msgpack {
($msgpack_data:expr) => {
Ok::<impulse_utils::responses::MsgPack<_>, impulse_utils::errors::ServerError>(impulse_utils::responses::MsgPack(
$msgpack_data,
$crate::fn_name!(),
))
};
}
#[cfg(all(feature = "salvo", feature = "mresult"))]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
impl<T: serde::Serialize + Send> ExplicitServerWrite for MsgPack<T> {
async fn explicit_write(self, res: &mut Response) {
res.status_code(salvo::http::StatusCode::OK);
match rmp_serde::to_vec(&self.0) {
Ok(bytes) => {
res.headers_mut().insert(
CONTENT_TYPE,
HeaderValue::from_static("application/msgpack; charset=utf-8"),
);
tracing::trace!("[{}] => Sending bytes: {:04X?}", self.1, bytes);
res.write_body(bytes).ok();
tracing::trace!("[{}] => Received and sent result 200 with MsgPack", self.1);
}
Err(e) => {
tracing::error!("[{}] => Failed to serialize data: {:?}", e, self.1);
crate::prelude::ServerError::from_private(e)
.with_public("Failed to serialize data.")
.with_500()
.explicit_write(res)
.await;
}
}
}
}
#[cfg(all(feature = "salvo", feature = "mresult"))]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[salvo::async_trait]
impl<T: serde::Serialize + Send> ServerResponseWriter for MsgPack<T> {
async fn write(self, _req: &mut Request, _depot: &mut Depot, res: &mut Response) {
ExplicitServerWrite::explicit_write(self, res).await
}
}
#[cfg(all(feature = "reqwest", feature = "cresult"))]
#[allow(async_fn_in_trait)]
pub trait MsgPackResponse {
async fn msgpack<T: serde::de::DeserializeOwned>(self) -> crate::prelude::CResult<T>;
}
#[cfg(all(feature = "reqwest", feature = "cresult"))]
impl MsgPackResponse for reqwest::Response {
async fn msgpack<T: serde::de::DeserializeOwned>(self) -> crate::prelude::CResult<T> {
use crate::errors::ClientError;
let full = self.bytes().await.map_err(ClientError::from)?;
rmp_serde::from_slice(&full).map_err(ClientError::from)
}
}
#[cfg(all(feature = "reqwest", feature = "cresult"))]
#[allow(async_fn_in_trait)]
pub trait CollectServerError
where
Self: Sized,
{
async fn collect_server_error(self) -> crate::prelude::CResult<Self>;
}
#[cfg(all(feature = "reqwest", feature = "cresult"))]
impl CollectServerError for reqwest::Response {
async fn collect_server_error(self) -> crate::prelude::CResult<Self> {
use crate::errors::ClientError;
let status_code = self.status().as_u16();
if status_code >= 400 {
let ctype = self
.headers()
.get(reqwest::header::CONTENT_TYPE)
.and_then(|v| v.to_str().ok())
.and_then(|v| v.split(';').next())
.map(|v| v.to_string());
if ctype.as_ref().is_some_and(|ct| ct.as_str().eq("application/json")) {
let err_json = self
.json::<crate::errors::ErrorResponse>()
.await
.map_err(|_| ClientError::from_str(crate::errors::public_msg_from(&Some(status_code))))?;
return Err(ClientError::from_str(err_json.err));
}
Err(ClientError::from_str(crate::errors::public_msg_from(&Some(
status_code,
))))
} else {
Ok(self)
}
}
}
#[cfg(all(feature = "reqwest", feature = "mresult"))]
#[allow(async_fn_in_trait)]
pub trait RedirectServerError
where
Self: Sized,
{
async fn redirect_server_error(self) -> crate::prelude::MResult<Self>;
}
#[cfg(all(feature = "reqwest", feature = "mresult"))]
impl RedirectServerError for reqwest::Response {
async fn redirect_server_error(self) -> crate::prelude::MResult<Self> {
use crate::errors::ServerError;
let status_code = self.status();
if status_code.as_u16() >= 400 {
let ctype = self
.headers()
.get(reqwest::header::CONTENT_TYPE)
.and_then(|v| v.to_str().ok())
.and_then(|v| v.split(';').next())
.map(|v| v.to_string());
if ctype.as_ref().is_some_and(|ct| ct.as_str().eq("application/json")) {
let err_json = self.json::<crate::errors::ErrorResponse>().await.map_err(|e| {
ServerError::from_private(e)
.with_public(crate::errors::public_msg_from(&Some(status_code.as_u16())))
.with_code(status_code)
})?;
return Err(ServerError::from_public(err_json.err).with_code(status_code));
}
Err(ServerError::from_public(crate::errors::public_msg_from(&Some(status_code.as_u16()))).with_code(status_code))
} else {
Ok(self)
}
}
}