wae_https/response/
mod.rs1use http::{Response, StatusCode, header};
6use serde::Serialize;
7use std::path::Path;
8
9use crate::{ApiResponse, Body, full_body};
10
11pub struct Html<T>(pub T);
13
14pub struct Redirect;
16
17pub trait IntoResponse {
19 fn into_response(self) -> Response<Body>;
21}
22
23pub struct JsonResponse;
25
26impl JsonResponse {
27 pub fn success<T: Serialize>(data: T) -> Response<Body> {
29 let api_response = ApiResponse::success(data);
30 let status = if api_response.success { StatusCode::OK } else { StatusCode::BAD_REQUEST };
31 let body = serde_json::to_string(&api_response).unwrap_or_default();
32 Response::builder().status(status).header(header::CONTENT_TYPE, "application/json").body(full_body(body)).unwrap()
33 }
34
35 pub fn error(code: impl Into<String>, message: impl Into<String>) -> Response<Body> {
37 let api_response = ApiResponse::<()>::error(code, message);
38 let status = if api_response.success { StatusCode::OK } else { StatusCode::BAD_REQUEST };
39 let body = serde_json::to_string(&api_response).unwrap_or_default();
40 Response::builder().status(status).header(header::CONTENT_TYPE, "application/json").body(full_body(body)).unwrap()
41 }
42
43 pub fn not_found(message: impl Into<String>) -> Response<Body> {
45 let body = ApiResponse::<()>::error("NOT_FOUND", message);
46 Response::builder()
47 .status(StatusCode::NOT_FOUND)
48 .header(header::CONTENT_TYPE, "application/json")
49 .body(full_body(serde_json::to_string(&body).unwrap_or_default()))
50 .unwrap()
51 }
52
53 pub fn internal_error(message: impl Into<String>) -> Response<Body> {
55 let body = ApiResponse::<()>::error("INTERNAL_ERROR", message);
56 Response::builder()
57 .status(StatusCode::INTERNAL_SERVER_ERROR)
58 .header(header::CONTENT_TYPE, "application/json")
59 .body(full_body(serde_json::to_string(&body).unwrap_or_default()))
60 .unwrap()
61 }
62}
63
64pub struct Attachment {
68 filename: String,
69 content_type: String,
70 data: Vec<u8>,
71}
72
73impl Attachment {
74 pub fn new(filename: impl Into<String>, content_type: impl Into<String>, data: Vec<u8>) -> Self {
76 Self { filename: filename.into(), content_type: content_type.into(), data }
77 }
78
79 pub async fn from_path(path: impl AsRef<Path>, filename: Option<impl Into<String>>) -> std::io::Result<Self> {
81 let path = path.as_ref();
82 let data = tokio::fs::read(path).await?;
83 let filename = filename
84 .map(|f| f.into())
85 .unwrap_or_else(|| path.file_name().and_then(|n| n.to_str()).unwrap_or("download").to_string());
86 let content_type = mime_guess::from_path(path).first_or_octet_stream().to_string();
87 Ok(Self::new(filename, content_type, data))
88 }
89}
90
91impl IntoResponse for Attachment {
92 fn into_response(self) -> Response<Body> {
93 let disposition = format!("attachment; filename=\"{}\"", self.filename);
94 Response::builder()
95 .status(StatusCode::OK)
96 .header(header::CONTENT_TYPE, self.content_type)
97 .header(header::CONTENT_DISPOSITION, disposition)
98 .body(full_body(self.data))
99 .unwrap()
100 }
101}
102
103pub struct StreamResponse {
107 body: Body,
108 content_type: String,
109}
110
111impl StreamResponse {
112 pub fn new(body: Body, content_type: impl Into<String>) -> Self {
114 Self { body, content_type: content_type.into() }
115 }
116}
117
118impl IntoResponse for StreamResponse {
119 fn into_response(self) -> Response<Body> {
120 Response::builder().status(StatusCode::OK).header(header::CONTENT_TYPE, self.content_type).body(self.body).unwrap()
121 }
122}