1use std::fmt::{self, Display};
2
3use actix_web::{
4 http::{
5 header::{ContentDisposition, DispositionParam, DispositionType},
6 StatusCode,
7 },
8 HttpResponse, HttpResponseBuilder,
9};
10use futures::{future, stream::once};
11
12pub type RspResult<T> = Result<T, ResponseError>;
13
14#[derive(Debug)]
15pub struct ResponseError(anyhow::Error);
16
17impl Display for ResponseError {
18 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19 f.write_str(&self.0.to_string())
20 }
21}
22
23impl actix_web::ResponseError for ResponseError {
24 fn status_code(&self) -> StatusCode {
25 StatusCode::INTERNAL_SERVER_ERROR
26 }
27
28 fn error_response(&self) -> HttpResponse {
29 HttpResponse::build(self.status_code()).finish()
30 }
31}
32
33impl<T> From<T> for ResponseError
34where
35 T: Into<anyhow::Error>,
36{
37 fn from(t: T) -> Self {
38 Self(t.into())
39 }
40}
41
42pub trait ResponseCodeTrait {
43 fn code(&self) -> i64;
44 fn message(&self) -> &'static str;
45}
46
47pub type ResponseBuilderFn = Box<dyn Fn(&mut HttpResponseBuilder)>;
48
49pub struct Response<T> {
50 pub http_code: u16,
51 pub code: i64,
52 pub message: String,
53 pub data: Option<T>,
54 pub builder: Option<ResponseBuilderFn>,
55 #[cfg(feature = "i18n")]
56 pub translate: bool,
57}
58
59impl<T> Response<T> {
60 pub fn new<C>(r: C) -> Self
61 where
62 C: ResponseCodeTrait,
63 {
64 Self {
65 http_code: 200,
66 code: r.code(),
67 message: r.message().to_owned(),
68 data: None,
69 builder: None,
70 #[cfg(feature = "i18n")]
71 translate: true,
72 }
73 }
74
75 pub fn new_code(code: u16) -> Self {
76 Self {
77 http_code: code,
78 code: 0,
79 message: String::new(),
80 data: None,
81 builder: None,
82 #[cfg(feature = "i18n")]
83 translate: false,
84 }
85 }
86
87 pub fn bad_request<S: Into<String>>(s: S) -> Self {
88 Self::new_code(400).message(s)
89 }
90
91 pub fn not_found() -> Self {
92 Self::new_code(404)
93 }
94
95 pub fn builder<F>(mut self, f: F) -> Self
96 where
97 F: Fn(&mut HttpResponseBuilder) + 'static,
98 {
99 self.builder = Some(Box::new(f));
100 self
101 }
102
103 pub fn message<S: Into<String>>(mut self, s: S) -> Self {
104 self.message = s.into();
105 self
106 }
107
108 pub fn data(mut self, data: T) -> Self {
109 self.data = Some(data);
110 self
111 }
112
113 pub fn file(name: String, data: Vec<u8>) -> HttpResponse {
114 let body = once(future::ok::<_, actix_web::Error>(data.into()));
115 let header = ContentDisposition {
116 disposition: DispositionType::Attachment,
117 parameters: vec![DispositionParam::Filename(name)],
118 };
119 HttpResponse::Ok()
120 .insert_header(("Content-Disposition", header))
121 .content_type("application/octet-stream")
122 .streaming(body)
123 }
124
125 #[cfg(feature = "i18n")]
126 pub fn translate(mut self) -> Self {
127 self.translate = true;
128 self
129 }
130
131 #[cfg(feature = "i18n")]
132 pub fn i18n_message(&self, req: &actix_web::HttpRequest) -> String {
133 use actix_web::HttpMessage as _;
134
135 if self.translate {
136 req.app_data::<actix_web::web::Data<crate::state::GlobalState>>()
137 .map_or_else(
138 || self.message.clone(),
139 |state| {
140 if let Some(ext) = req
141 .extensions()
142 .get::<std::sync::Arc<crate::request::Extension>>()
143 {
144 crate::t!(state.locale, &self.message, &ext.lang)
145 } else {
146 self.message.clone()
147 }
148 },
149 )
150 } else {
151 self.message.clone()
152 }
153 }
154}
155
156#[cfg(feature = "response-json")]
157pub type JsonResponse = Response<serde_json::Value>;
158
159#[cfg(feature = "response-json")]
160impl JsonResponse {
161 pub fn json<T: serde::Serialize>(mut self, data: T) -> Self {
162 self.data = Some(serde_json::json!(data));
163 self
164 }
165}
166
167#[cfg(feature = "response-json")]
168impl actix_web::Responder for JsonResponse {
169 type Body = actix_web::body::EitherBody<String>;
170
171 fn respond_to(
172 self,
173 #[allow(unused_variables)] req: &actix_web::HttpRequest,
174 ) -> HttpResponse<Self::Body> {
175 if self.http_code == 200 {
176 #[cfg(feature = "i18n")]
177 let message = self.i18n_message(req);
178 #[cfg(not(feature = "i18n"))]
179 let message = self.message;
180 let mut body = serde_json::json!({
181 "code": self.code,
182 "message": message,
183 });
184 if let Some(data) = self.data {
185 body.as_object_mut()
186 .unwrap()
187 .insert(String::from("data"), data);
188 }
189 let body = body.to_string();
190 let mut rsp =
191 HttpResponse::build(actix_web::http::StatusCode::from_u16(self.http_code).unwrap());
192 rsp.content_type(actix_web::http::header::ContentType::json());
193 if let Some(builder) = self.builder {
194 builder(&mut rsp);
195 }
196 rsp.message_body(body).unwrap().map_into_left_body()
197 } else {
198 HttpResponse::build(actix_web::http::StatusCode::from_u16(self.http_code).unwrap())
199 .message_body(self.message)
200 .unwrap()
201 .map_into_left_body()
202 }
203 }
204}