Skip to main content

nimble_http/
response.rs

1//! response.rs
2
3use hyper::{Body, Response, StatusCode, header};
4use mime_guess::from_path as get_mine_from_path;
5use serde::Serialize;
6use std::fs::read_to_string as read_file;
7
8/// 转换为 HTTP 响应的 trait
9pub trait IntoResponse {
10	fn into_response(self) -> Response<Body>;
11}
12
13// 为 &str 实现
14impl IntoResponse for &'static str {
15	fn into_response(self) -> Response<Body> {
16		Response::builder()
17			.header(header::CONTENT_TYPE, "text/plain; charset=utf-8")
18			.body(Body::from(self))
19			.unwrap()
20	}
21}
22
23// 为 String 实现
24impl IntoResponse for String {
25	fn into_response(self) -> Response<Body> {
26		Response::builder()
27			.header(header::CONTENT_TYPE, "text/plain; charset=utf-8")
28			.body(Body::from(self))
29			.unwrap()
30	}
31}
32
33// 为 Vec<u8> 实现
34impl IntoResponse for Vec<u8> {
35	fn into_response(self) -> Response<Body> {
36		Response::builder().body(Body::from(self)).unwrap()
37	}
38}
39
40// 为 () 实现(空响应)
41impl IntoResponse for () {
42	fn into_response(self) -> Response<Body> {
43		Response::builder()
44			.status(StatusCode::NO_CONTENT)
45			.body(Body::empty())
46			.unwrap()
47	}
48}
49
50// 为 ([header1,header2...], String) 实现
51impl<const N: usize> IntoResponse for ([(header::HeaderName, &'static str); N], String) {
52	fn into_response(self) -> Response<Body> {
53		let (headers, body) = self;
54		let mut builder = Response::builder();
55
56		for (name, value) in headers {
57			builder = builder.header(name, value);
58		}
59
60		builder.body(Body::from(body)).unwrap()
61	}
62}
63
64// 为 ([header1,header2...], StatusCode, String) 实现
65impl<const N: usize> IntoResponse for ([(header::HeaderName, &'static str); N], StatusCode, String) {
66    fn into_response(self) -> Response<Body> {
67        let (headers, status, body) = self;
68        let mut builder = Response::builder().status(status);
69
70        for (name, value) in headers {
71            builder = builder.header(name, value);
72        }
73
74        builder.body(Body::from(body)).unwrap()
75    }
76}
77
78// 为 StatusCode 实现(仅状态码)
79impl IntoResponse for StatusCode {
80	fn into_response(self) -> Response<Body> {
81		Response::builder()
82			.status(self)
83			.body(Body::empty())
84			.unwrap()
85	}
86}
87
88/// JSON 响应包装器
89pub struct Json<T: Serialize>(pub T);
90
91impl<T: Serialize> IntoResponse for Json<T> {
92	fn into_response(self) -> Response<Body> {
93		let json = serde_json::to_string(&self.0).unwrap();
94		Response::builder()
95			.header(header::CONTENT_TYPE, "application/json")
96			.body(Body::from(json))
97			.unwrap()
98	}
99}
100
101/// HTML 响应包装器
102pub struct Html(pub String);
103
104impl IntoResponse for Html {
105	fn into_response(self) -> Response<Body> {
106		Response::builder()
107			.header(header::CONTENT_TYPE, "text/html; charset=utf-8")
108			.body(Body::from(self.0))
109			.unwrap()
110	}
111}
112
113/// 文本响应包装器
114pub struct Text(pub String);
115
116impl IntoResponse for Text {
117	fn into_response(self) -> Response<Body> {
118		Response::builder()
119			.header(header::CONTENT_TYPE, "text/plain; charset=utf-8")
120			.body(Body::from(self.0))
121			.unwrap()
122	}
123}
124
125// 为 Result 实现
126impl<T, E> IntoResponse for Result<T, E>
127where
128	T: IntoResponse,
129	E: IntoResponse,
130{
131	fn into_response(self) -> Response<Body> {
132		match self {
133			Ok(value) => value.into_response(),
134			Err(err) => err.into_response(),
135		}
136	}
137}
138
139// 重定向url包装器
140pub struct Redirect(pub String);
141
142impl IntoResponse for Redirect {
143	fn into_response(self) -> Response<Body> {
144		Response::builder()
145			.status(302)
146			.header("Location", self.0)
147			.body(Body::empty())
148			.unwrap()
149	}
150}
151
152// 永久重定向url包装器
153pub struct PermRedirect(pub String);
154
155impl IntoResponse for PermRedirect {
156	fn into_response(self) -> Response<Body> {
157		Response::builder()
158			.status(301)
159			.header("Location", self.0)
160			.body(Body::empty())
161			.unwrap()
162	}
163}
164
165impl Redirect {
166	pub fn perm(url: String) -> PermRedirect {
167		PermRedirect(url)
168	}
169}
170
171// 文件包装器
172pub struct File(pub String, pub bool);
173
174impl IntoResponse for File {
175	fn into_response(self) -> Response<Body> {
176		Response::builder()
177			.header(
178				header::CONTENT_TYPE,
179				if self.1 {
180					"application/octet-stream; charset=utf-8".to_string()
181				} else {
182					format!(
183						"{}; charset=utf-8",
184						get_mine_from_path(&self.0)
185							.first_or_octet_stream()
186							.to_string()
187					)
188				},
189			)
190			.body(Body::from(read_file(self.0).unwrap_or(String::new())))
191			.unwrap()
192	}
193}