1use std::num::NonZeroU16;
3
4use super::{body::Body, headers::Headers};
5use crate::{ext::CodeExt, Error};
6
7#[derive(Debug, Clone, PartialEq)]
9pub struct Response {
10 pub status: StatusCode,
11 pub headers: Headers,
12 pub body: Body,
13 pub message: Option<String>,
14}
15
16impl Response {
17 pub fn new(body: impl Into<Body>) -> Self {
18 Self {
19 status: StatusCode::default(),
20 headers: Headers::default(),
21 body: body.into(),
22 message: None,
23 }
24 }
25
26 pub fn with_status(mut self, status: impl Into<StatusCode>) -> Self {
27 self.status = status.into();
28 self
29 }
30
31 pub fn with_headers(mut self, headers: Headers) -> Self {
32 self.headers = headers;
33 self
34 }
35
36 pub fn with_message(mut self, message: impl Into<String>) -> Self {
37 self.message = Some(message.into());
38 self
39 }
40
41 pub fn status(&self) -> &StatusCode {
42 &self.status
43 }
44
45 pub fn headers(&self) -> &Headers {
46 &self.headers
47 }
48
49 pub fn body(&self) -> &Body {
50 &self.body
51 }
52
53 pub fn message(&self) -> Option<&str> {
54 self.message.as_deref()
55 }
56
57 pub fn is_ok(&self) -> bool {
58 self.status.is_ok()
59 }
60
61 pub fn is_error(&self) -> bool {
62 self.status.is_error()
63 }
64}
65
66impl Default for Response {
67 fn default() -> Self {
68 Self {
69 status: StatusCode::OK,
70 headers: Headers::default(),
71 body: Body::default(),
72 message: None,
73 }
74 }
75}
76
77#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
79pub struct StatusCode(NonZeroU16);
80
81impl StatusCode {
82 pub fn from_u16(code: u16) -> Result<Self, Error> {
83 if !(100..1000).contains(&code) {
84 return Err(Error::Invalid("invalid status code".into()));
85 }
86 Ok(Self(NonZeroU16::new(code).unwrap()))
87 }
88
89 pub fn as_u16(&self) -> u16 {
90 self.0.get()
91 }
92
93 pub fn is_informational(&self) -> bool {
94 (100..200).contains(&self.as_u16())
95 }
96
97 pub fn is_success(&self) -> bool {
98 (200..300).contains(&self.as_u16())
99 }
100
101 pub fn is_redirection(&self) -> bool {
102 (300..400).contains(&self.as_u16())
103 }
104
105 pub fn is_error(&self) -> bool {
106 (400..600).contains(&self.as_u16())
107 }
108
109 pub fn is_ok(&self) -> bool {
110 self.is_success()
111 }
112
113 pub fn as_http(&self) -> http::StatusCode {
114 http::StatusCode::from_u16(self.as_u16()).unwrap()
115 }
116
117 pub fn as_grpc(&self) -> tonic::Code {
118 tonic::Code::from_u16(self.as_u16()).unwrap()
119 }
120
121 pub fn as_grpc_i32(&self) -> i32 {
122 self.as_grpc() as i32
123 }
124}
125
126impl StatusCode {
127 pub const CONTINUE: StatusCode = StatusCode(NonZeroU16::new(100).unwrap());
128 pub const SWITCHING_PROTOCOLS: StatusCode = StatusCode(NonZeroU16::new(101).unwrap());
129 pub const PROCESSING: StatusCode = StatusCode(NonZeroU16::new(102).unwrap());
130 pub const EARLY_HINTS: StatusCode = StatusCode(NonZeroU16::new(103).unwrap());
131
132 pub const OK: StatusCode = StatusCode(NonZeroU16::new(200).unwrap());
133 pub const CREATED: StatusCode = StatusCode(NonZeroU16::new(201).unwrap());
134 pub const ACCEPTED: StatusCode = StatusCode(NonZeroU16::new(202).unwrap());
135 pub const NON_AUTHORITATIVE_INFORMATION: StatusCode = StatusCode(NonZeroU16::new(203).unwrap());
136 pub const NO_CONTENT: StatusCode = StatusCode(NonZeroU16::new(204).unwrap());
137 pub const RESET_CONTENT: StatusCode = StatusCode(NonZeroU16::new(205).unwrap());
138 pub const PARTIAL_CONTENT: StatusCode = StatusCode(NonZeroU16::new(206).unwrap());
139 pub const MULTI_STATUS: StatusCode = StatusCode(NonZeroU16::new(207).unwrap());
140 pub const ALREADY_REPORTED: StatusCode = StatusCode(NonZeroU16::new(208).unwrap());
141 pub const IM_USED: StatusCode = StatusCode(NonZeroU16::new(226).unwrap());
142
143 pub const MULTIPLE_CHOICES: StatusCode = StatusCode(NonZeroU16::new(300).unwrap());
144 pub const MOVED_PERMANENTLY: StatusCode = StatusCode(NonZeroU16::new(301).unwrap());
145 pub const FOUND: StatusCode = StatusCode(NonZeroU16::new(302).unwrap());
146 pub const SEE_OTHER: StatusCode = StatusCode(NonZeroU16::new(303).unwrap());
147 pub const NOT_MODIFIED: StatusCode = StatusCode(NonZeroU16::new(304).unwrap());
148 pub const USE_PROXY: StatusCode = StatusCode(NonZeroU16::new(305).unwrap());
149 pub const TEMPORARY_REDIRECT: StatusCode = StatusCode(NonZeroU16::new(307).unwrap());
150 pub const PERMANENT_REDIRECT: StatusCode = StatusCode(NonZeroU16::new(308).unwrap());
151
152 pub const BAD_REQUEST: StatusCode = StatusCode(NonZeroU16::new(400).unwrap());
153 pub const UNAUTHORIZED: StatusCode = StatusCode(NonZeroU16::new(401).unwrap());
154 pub const PAYMENT_REQUIRED: StatusCode = StatusCode(NonZeroU16::new(402).unwrap());
155 pub const FORBIDDEN: StatusCode = StatusCode(NonZeroU16::new(403).unwrap());
156 pub const NOT_FOUND: StatusCode = StatusCode(NonZeroU16::new(404).unwrap());
157 pub const METHOD_NOT_ALLOWED: StatusCode = StatusCode(NonZeroU16::new(405).unwrap());
158 pub const NOT_ACCEPTABLE: StatusCode = StatusCode(NonZeroU16::new(406).unwrap());
159 pub const PROXY_AUTHENTICATION_REQUIRED: StatusCode = StatusCode(NonZeroU16::new(407).unwrap());
160 pub const REQUEST_TIMEOUT: StatusCode = StatusCode(NonZeroU16::new(408).unwrap());
161 pub const CONFLICT: StatusCode = StatusCode(NonZeroU16::new(409).unwrap());
162 pub const GONE: StatusCode = StatusCode(NonZeroU16::new(410).unwrap());
163 pub const LENGTH_REQUIRED: StatusCode = StatusCode(NonZeroU16::new(411).unwrap());
164 pub const PRECONDITION_FAILED: StatusCode = StatusCode(NonZeroU16::new(412).unwrap());
165 pub const PAYLOAD_TOO_LARGE: StatusCode = StatusCode(NonZeroU16::new(413).unwrap());
166 pub const URI_TOO_LONG: StatusCode = StatusCode(NonZeroU16::new(414).unwrap());
167 pub const UNSUPPORTED_MEDIA_TYPE: StatusCode = StatusCode(NonZeroU16::new(415).unwrap());
168 pub const RANGE_NOT_SATISFIABLE: StatusCode = StatusCode(NonZeroU16::new(416).unwrap());
169 pub const EXPECTATION_FAILED: StatusCode = StatusCode(NonZeroU16::new(417).unwrap());
170 pub const IM_A_TEAPOT: StatusCode = StatusCode(NonZeroU16::new(418).unwrap());
171 pub const MISDIRECTED_REQUEST: StatusCode = StatusCode(NonZeroU16::new(421).unwrap());
172 pub const UNPROCESSABLE_ENTITY: StatusCode = StatusCode(NonZeroU16::new(422).unwrap());
173 pub const LOCKED: StatusCode = StatusCode(NonZeroU16::new(423).unwrap());
174 pub const FAILED_DEPENDENCY: StatusCode = StatusCode(NonZeroU16::new(424).unwrap());
175 pub const TOO_EARLY: StatusCode = StatusCode(NonZeroU16::new(425).unwrap());
176 pub const UPGRADE_REQUIRED: StatusCode = StatusCode(NonZeroU16::new(426).unwrap());
177 pub const PRECONDITION_REQUIRED: StatusCode = StatusCode(NonZeroU16::new(428).unwrap());
178 pub const TOO_MANY_REQUESTS: StatusCode = StatusCode(NonZeroU16::new(429).unwrap());
179 pub const REQUEST_HEADER_FIELDS_TOO_LARGE: StatusCode = StatusCode(NonZeroU16::new(431).unwrap());
180 pub const UNAVAILABLE_FOR_LEGAL_REASONS: StatusCode = StatusCode(NonZeroU16::new(451).unwrap());
181
182 pub const INTERNAL_SERVER_ERROR: StatusCode = StatusCode(NonZeroU16::new(500).unwrap());
183 pub const NOT_IMPLEMENTED: StatusCode = StatusCode(NonZeroU16::new(501).unwrap());
184 pub const BAD_GATEWAY: StatusCode = StatusCode(NonZeroU16::new(502).unwrap());
185 pub const SERVICE_UNAVAILABLE: StatusCode = StatusCode(NonZeroU16::new(503).unwrap());
186 pub const GATEWAY_TIMEOUT: StatusCode = StatusCode(NonZeroU16::new(504).unwrap());
187 pub const HTTP_VERSION_NOT_SUPPORTED: StatusCode = StatusCode(NonZeroU16::new(505).unwrap());
188 pub const VARIANT_ALSO_NEGOTIATES: StatusCode = StatusCode(NonZeroU16::new(506).unwrap());
189 pub const INSUFFICIENT_STORAGE: StatusCode = StatusCode(NonZeroU16::new(507).unwrap());
190 pub const LOOP_DETECTED: StatusCode = StatusCode(NonZeroU16::new(508).unwrap());
191 pub const NOT_EXTENDED: StatusCode = StatusCode(NonZeroU16::new(510).unwrap());
192 pub const NETWORK_AUTHENTICATION_REQUIRED: StatusCode = StatusCode(NonZeroU16::new(511).unwrap());
193}
194
195impl Default for StatusCode {
196 fn default() -> Self {
197 Self::OK
198 }
199}
200
201impl PartialEq<u16> for StatusCode {
202 fn eq(&self, other: &u16) -> bool {
203 self.as_u16() == *other
204 }
205}
206
207impl PartialEq<StatusCode> for u16 {
208 fn eq(&self, other: &StatusCode) -> bool {
209 *self == other.as_u16()
210 }
211}
212
213impl PartialEq<StatusCode> for http::StatusCode {
214 fn eq(&self, other: &StatusCode) -> bool {
215 *self == other.as_u16()
216 }
217}
218
219impl TryFrom<u16> for StatusCode {
220 type Error = Error;
221
222 fn try_from(value: u16) -> Result<Self, Self::Error> {
223 Self::from_u16(value)
224 }
225}
226
227impl From<StatusCode> for u16 {
228 fn from(status: StatusCode) -> u16 {
229 status.0.get()
230 }
231}
232
233impl From<http::StatusCode> for StatusCode {
234 fn from(value: http::StatusCode) -> Self {
235 Self::from_u16(value.as_u16()).unwrap()
236 }
237}
238
239impl From<StatusCode> for http::StatusCode {
240 fn from(value: StatusCode) -> Self {
241 Self::from_u16(value.0.into()).unwrap()
242 }
243}
244
245impl From<StatusCode> for tonic::Code {
246 fn from(value: StatusCode) -> Self {
247 tonic::Code::from_u16(value.as_u16()).unwrap()
248 }
249}