1use crate::prelude::*;
4
5#[cfg(feature = "salvo")]
6#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
7use salvo::http::HeaderValue;
8
9#[cfg(feature = "salvo")]
10#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
11use salvo::hyper::header::CONTENT_TYPE;
12
13#[cfg(feature = "salvo")]
14#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
15use salvo::oapi::{EndpointOutRegister, ToSchema};
16
17#[cfg(feature = "salvo")]
18#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
19use salvo::{Request, Response, Depot};
20
21#[cfg(feature = "salvo")]
22#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
23use salvo::Writer as ServerResponseWriter;
24
25#[cfg(feature = "salvo")]
26#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
27use salvo::fs::NamedFile;
28
29#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
30use serde::Serialize;
31
32#[cfg(feature = "reqwest")]
33#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
34use serde::de::DeserializeOwned;
35
36#[macro_export]
38macro_rules! fn_name {
39 () => {{
40 fn f() {}
41 fn type_name_of<T>(_: T) -> &'static str {
42 std::any::type_name::<T>()
43 }
44 let name = type_name_of(f);
45
46 match name[..name.len() - 3].rsplit("::").nth(2) {
48 Some(el) => el,
49 None => &name[..name.len() - 3],
50 }
51 }};
52}
53
54#[cfg(feature = "salvo")]
56macro_rules! impl_oapi_endpoint_out {
57 ($t:tt, $c:expr) => {
58 impl EndpointOutRegister for $t {
59 #[inline]
60 fn register(components: &mut salvo::oapi::Components, operation: &mut salvo::oapi::Operation) {
61 operation.responses.insert(
62 "200",
63 salvo::oapi::Response::new("Ok").add_content($c, String::to_schema(components)),
64 );
65 }
66 }
67 };
68}
69
70#[cfg(feature = "salvo")]
72macro_rules! impl_oapi_endpoint_out_t {
73 ($t:tt, $c:expr) => {
74 impl<T> EndpointOutRegister for $t<T> {
75 #[inline]
76 fn register(components: &mut salvo::oapi::Components, operation: &mut salvo::oapi::Operation) {
77 operation.responses.insert(
78 "200",
79 salvo::oapi::Response::new("Ok").add_content($c, String::to_schema(components)),
80 );
81 }
82 }
83 };
84}
85
86#[cfg(feature = "salvo")]
88#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
89pub struct OK(pub &'static str);
90
91#[cfg(feature = "salvo")]
92#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
93impl_oapi_endpoint_out!(OK, "text/plain");
94
95#[cfg(feature = "salvo")]
96#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
97#[macro_export]
98macro_rules! ok { () => { Ok(OK(cc_utils::fn_name!())) }; }
99
100#[cfg(feature = "salvo")]
101#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
102#[salvo::async_trait]
103impl ServerResponseWriter for OK {
104 async fn write(self, _req: &mut Request, _depot: &mut Depot, res: &mut Response) {
105 res.status_code(StatusCode::OK);
106 res.render("");
107 log::debug!("[{}] => Received and sent result 200", self.0);
108 }
109}
110
111#[cfg(feature = "salvo")]
113#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
114#[derive(Debug)]
115pub struct Plain(pub String, pub &'static str);
116
117#[cfg(feature = "salvo")]
118#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
119impl_oapi_endpoint_out!(Plain, "text/plain");
120
121#[cfg(feature = "salvo")]
122#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
123#[macro_export]
124macro_rules! plain { ($e:expr) => { Ok(Plain($e, cc_utils::fn_name!())) }; }
125
126#[cfg(feature = "salvo")]
127#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
128#[salvo::async_trait]
129impl ServerResponseWriter for Plain {
130 async fn write(self, _req: &mut Request, _depot: &mut Depot, res: &mut Response) {
131 res.status_code(StatusCode::OK);
132 res.render(&self.0);
133 log::debug!("[{}] => Received and sent result 200 with text: {}", self.1, self.0);
134 }
135}
136
137#[cfg(feature = "salvo")]
139#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
140#[derive(Debug)]
141pub struct Html(pub String, pub &'static str);
142
143#[cfg(feature = "salvo")]
144#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
145impl_oapi_endpoint_out!(Html, "text/html");
146
147#[cfg(feature = "salvo")]
148#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
149#[macro_export]
150macro_rules! html { ($e:expr) => { Ok(Html($e, cc_utils::fn_name!())) }; }
151
152#[cfg(feature = "salvo")]
153#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
154#[salvo::async_trait]
155impl ServerResponseWriter for Html {
156 async fn write(self, _req: &mut Request, _depot: &mut Depot, res: &mut Response) {
157 res.status_code(StatusCode::OK);
158 res.render(salvo::writing::Text::Html(&self.0));
159 log::debug!("[{}] => Received and sent result 200 with HTML", self.1);
160 }
161}
162
163#[cfg(feature = "salvo")]
165#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
166#[derive(Debug)]
167pub struct File(pub String, pub String, pub &'static str);
168
169#[cfg(feature = "salvo")]
170#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
171impl_oapi_endpoint_out!(File, "application/octet-stream");
172
173#[cfg(feature = "salvo")]
186#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
187#[macro_export]
188macro_rules! file { ($e1:expr, $e2:expr) => { Ok(File($e1, $e2, cc_utils::fn_name!())) }; }
189
190#[cfg(feature = "salvo")]
191#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
192#[salvo::async_trait]
193impl ServerResponseWriter for File {
194 async fn write(self, req: &mut Request, _depot: &mut Depot, res: &mut Response) {
195 res.status_code(StatusCode::OK);
196 NamedFile::builder(&self.0).attached_name(&self.1).use_last_modified(true).send(req.headers(), res).await;
197 log::debug!("[{}] => Received and sent result 200 with file {}", self.2, self.1);
198 }
199}
200
201#[cfg(feature = "salvo")]
203#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
204#[derive(Debug)]
205pub struct Json<T>(pub T, pub &'static str);
206
207#[cfg(feature = "salvo")]
208#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
209impl_oapi_endpoint_out_t!(Json, "application/json");
210
211#[cfg(feature = "salvo")]
212#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
213#[macro_export]
214macro_rules! json { ($e:expr) => { Ok(Json($e, cc_utils::fn_name!())) }; }
215
216#[cfg(feature = "salvo")]
217#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
218#[salvo::async_trait]
219impl<T: Serialize + Send> ServerResponseWriter for Json<T> {
220 async fn write(self, _req: &mut Request, _depot: &mut Depot, res: &mut Response) {
221 res.status_code(StatusCode::OK);
222 res.render(salvo::writing::Json(self.0));
223 log::debug!("[{}] => Received and sent result 200 with JSON", self.1);
224 }
225}
226
227#[cfg(feature = "salvo")]
229#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
230#[derive(Debug)]
231pub struct MsgPack<T>(pub T, pub &'static str);
232
233#[cfg(feature = "salvo")]
234#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
235impl_oapi_endpoint_out_t!(MsgPack, "application/msgpack");
236
237#[cfg(feature = "salvo")]
238#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
239#[macro_export]
240macro_rules! msgpack { ($e:expr) => { Ok(MsgPack($e, cc_utils::fn_name!())) }; }
241
242#[cfg(feature = "salvo")]
243#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
244#[salvo::async_trait]
245impl<T: Serialize + Send> ServerResponseWriter for MsgPack<T> {
246 async fn write(self, req: &mut Request, depot: &mut Depot, res: &mut Response) {
247 res.status_code(StatusCode::OK);
248 match rmp_serde::to_vec(&self.0) {
249 Ok(bytes) => {
250 res.headers_mut().insert(CONTENT_TYPE, HeaderValue::from_static("application/msgpack; charset=utf-8"));
251 log::debug!("[{}] => Sending bytes: {:?}", self.1, bytes);
252 res.write_body(bytes).ok();
253 log::debug!("[{}] => Received and sent result 200 with MsgPack", self.1);
254 }
255 Err(e) => {
256 log::error!("[{}] => Failed to serialize data: {:?}", e, self.1);
257 ErrorResponse::from("Failed to serialize data.").with_500().build().write(req, depot, res).await;
258 }
259 }
260 }
261}
262
263#[cfg(feature = "reqwest")]
264#[allow(async_fn_in_trait)]
265#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
266pub trait MsgPackResponse {
267 async fn msgpack<T: DeserializeOwned>(self) -> CResult<T>;
268}
269
270#[cfg(feature = "reqwest")]
271#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
272impl MsgPackResponse for reqwest::Response {
273 async fn msgpack<T: DeserializeOwned>(self) -> CResult<T> {
274 let full = self.bytes().await?;
275 rmp_serde::from_slice(&full).consider_cli(None)
276 }
277}