Skip to content

Commit e675bb0

Browse files
seqrem4txpre-commit-ci[bot]
authored
feat!: implement and handle IntoResponse (#256)
* feat: scaffolding for IntoResponse Am I cooking or not? # Conflicts: # Cargo.toml * feat: implement IntoResponse for common types * refactor: simplify implementation of macros for lists of arguments * feat: use IntoResponse in the handler * feat: simplify hello world example * feat: use Html for IntoResponse * chore: apply review suggestions * feat: make IntoResponse return cot::Result<Response> * chore: update the codebase to new IntoResponse * fix: leftover issues * fix: use the trait method * feat: improve header parsing * feat: use macro shorthand for openapi * test: IntoResponse * docs: IntoResponse * docs: make clippy happy * docs: make clippy happy v2 * docs: make clippy happy v3 * docs: make clippy happy v4 * chore: apply suggestions from code review Co-authored-by: Mateusz Maćkowski <m4tx@m4tx.pl> * chore: apply review suggestions * chore: make clippy happy v5 * chore: make clippy happy v6 * chore: make clippy happy v7 * chore: make clippy happy v8 * chore: apply suggestions from code review Co-authored-by: Mateusz Maćkowski <m4tx@m4tx.pl> * chore(pre-commit.ci): auto fixes from pre-commit hooks * chore: update cot/src/headers.rs Co-authored-by: Mateusz Maćkowski <m4tx@m4tx.pl> * chore: cargo fmt --------- Co-authored-by: Mateusz Maćkowski <m4tx@m4tx.pl> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent a9ea4f9 commit e675bb0

File tree

22 files changed

+944
-236
lines changed

22 files changed

+944
-236
lines changed

cot/src/admin.rs

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@ use crate::common_types::Password;
2626
use crate::form::{
2727
Form, FormContext, FormErrorTarget, FormField, FormFieldValidationError, FormResult,
2828
};
29+
use crate::html::Html;
2930
use crate::request::extractors::{FromRequestParts, Path, UrlQuery};
3031
use crate::request::{Request, RequestExt};
31-
use crate::response::{Response, ResponseExt};
32+
use crate::response::{IntoResponse, Response};
3233
use crate::router::{Router, Urls};
33-
use crate::{App, Body, Error, Method, RequestHandler, StatusCode, reverse_redirect, static_files};
34+
use crate::{App, Error, Method, RequestHandler, reverse_redirect, static_files};
3435

3536
struct AdminAuthenticated<T, H: Send + Sync>(H, PhantomData<fn() -> T>);
3637

@@ -55,7 +56,7 @@ impl<T, H: RequestHandler<T> + Send + Sync> RequestHandler<T> for AdminAuthentic
5556
async fn index(
5657
urls: Urls,
5758
AdminModelManagers(managers): AdminModelManagers,
58-
) -> crate::Result<Response> {
59+
) -> crate::Result<Html> {
5960
#[derive(Debug, Template)]
6061
#[template(path = "admin/model_list.html")]
6162
struct ModelListTemplate<'a> {
@@ -68,10 +69,7 @@ async fn index(
6869
urls: &urls,
6970
model_managers: managers,
7071
};
71-
Ok(Response::new_html(
72-
StatusCode::OK,
73-
Body::fixed(template.render()?),
74-
))
72+
Ok(Html::new(template.render()?))
7573
}
7674

7775
#[derive(Debug, Form)]
@@ -115,10 +113,7 @@ async fn login(urls: Urls, auth: Auth, mut request: Request) -> crate::Result<Re
115113
urls: &urls,
116114
form: login_form_context,
117115
};
118-
Ok(Response::new_html(
119-
StatusCode::OK,
120-
Body::fixed(template.render()?),
121-
))
116+
Html::new(template.render()?).into_response()
122117
}
123118

124119
async fn authenticate(auth: &Auth, login_form: LoginForm) -> cot::Result<bool> {
@@ -226,10 +221,7 @@ async fn view_model(
226221
total_pages,
227222
};
228223

229-
Ok(Response::new_html(
230-
StatusCode::OK,
231-
Body::fixed(template.render()?),
232-
))
224+
Html::new(template.render()?).into_response()
233225
}
234226

235227
async fn create_model_instance(
@@ -296,10 +288,7 @@ async fn edit_model_instance_impl(
296288
is_edit: object_id.is_some(),
297289
};
298290

299-
Ok(Response::new_html(
300-
StatusCode::OK,
301-
Body::fixed(template.render()?),
302-
))
291+
Html::new(template.render()?).into_response()
303292
}
304293

305294
async fn remove_model_instance(
@@ -336,10 +325,7 @@ async fn remove_model_instance(
336325
object: &*object,
337326
};
338327

339-
Ok(Response::new_html(
340-
StatusCode::OK,
341-
Body::fixed(template.render()?),
342-
))
328+
Html::new(template.render()?).into_response()
343329
}
344330
}
345331

cot/src/body.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,24 @@ impl http_body::Body for Body {
243243
}
244244
}
245245

246+
macro_rules! body_from_impl {
247+
($ty:ty) => {
248+
impl From<$ty> for Body {
249+
fn from(buf: $ty) -> Self {
250+
Self::new(BodyInner::Fixed(Bytes::from(buf)))
251+
}
252+
}
253+
};
254+
}
255+
256+
body_from_impl!(&'static [u8]);
257+
body_from_impl!(Vec<u8>);
258+
259+
body_from_impl!(&'static str);
260+
body_from_impl!(String);
261+
262+
body_from_impl!(Bytes);
263+
246264
#[cfg(test)]
247265
mod tests {
248266
use std::pin::Pin;

cot/src/handler.rs

Lines changed: 98 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use tower::util::BoxCloneSyncService;
66

77
use crate::request::Request;
88
use crate::request::extractors::{FromRequest, FromRequestParts};
9-
use crate::response::Response;
9+
use crate::response::{IntoResponse, Response};
1010
use crate::{Error, Result};
1111

1212
/// A function that takes a request and returns a response.
@@ -59,11 +59,12 @@ pub(crate) fn into_box_request_handler<T, H: RequestHandler<T> + Send + Sync>(
5959

6060
macro_rules! impl_request_handler {
6161
($($ty:ident),*) => {
62-
impl<T, $($ty,)* R> RequestHandler<($($ty,)*)> for T
62+
impl<Func, $($ty,)* Fut, R> RequestHandler<($($ty,)*)> for Func
6363
where
64-
T: FnOnce($($ty,)*) -> R + Clone + Send + Sync + 'static,
64+
Func: FnOnce($($ty,)*) -> Fut + Clone + Send + Sync + 'static,
6565
$($ty: FromRequestParts + Send,)*
66-
R: for<'a> Future<Output = Result<Response>> + Send,
66+
Fut: Future<Output = R> + Send,
67+
R: IntoResponse,
6768
{
6869
#[allow(non_snake_case)]
6970
async fn handle(&self, request: Request) -> Result<Response> {
@@ -74,21 +75,22 @@ macro_rules! impl_request_handler {
7475
let $ty = $ty::from_request_parts(&mut parts).await?;
7576
)*
7677

77-
self.clone()($($ty,)*).await
78+
self.clone()($($ty,)*).await.into_response()
7879
}
7980
}
8081
};
8182
}
8283

8384
macro_rules! impl_request_handler_from_request {
8485
($($ty_lhs:ident,)* ($ty_from_request:ident) $(,$ty_rhs:ident)*) => {
85-
impl<T, $($ty_lhs,)* $ty_from_request, $($ty_rhs,)* R> RequestHandler<($($ty_lhs,)* $ty_from_request, (), $($ty_rhs,)*)> for T
86+
impl<Func, $($ty_lhs,)* $ty_from_request, $($ty_rhs,)* Fut, R> RequestHandler<($($ty_lhs,)* $ty_from_request, (), $($ty_rhs,)*)> for Func
8687
where
87-
T: FnOnce($($ty_lhs,)* $ty_from_request, $($ty_rhs),*) -> R + Clone + Send + Sync + 'static,
88+
Func: FnOnce($($ty_lhs,)* $ty_from_request, $($ty_rhs),*) -> Fut + Clone + Send + Sync + 'static,
8889
$($ty_lhs: FromRequestParts + Send,)*
8990
$ty_from_request: FromRequest + Send,
9091
$($ty_rhs: FromRequestParts + Send,)*
91-
R: for<'a> Future<Output = Result<Response>> + Send,
92+
Fut: Future<Output = R> + Send,
93+
R: IntoResponse,
9294
{
9395
#[expect(non_snake_case)]
9496
async fn handle(&self, request: Request) -> Result<Response> {
@@ -105,79 +107,99 @@ macro_rules! impl_request_handler_from_request {
105107
let request = Request::from_parts(parts, body);
106108
let $ty_from_request = $ty_from_request::from_request(request).await?;
107109

108-
self.clone()($($ty_lhs,)* $ty_from_request, $($ty_rhs),*).await
110+
self.clone()($($ty_lhs,)* $ty_from_request, $($ty_rhs),*).await.into_response()
109111
}
110112
}
111113
};
112114
}
113115

114-
impl_request_handler!();
115-
impl_request_handler!(P1);
116-
impl_request_handler!(P1, P2);
117-
impl_request_handler!(P1, P2, P3);
118-
impl_request_handler!(P1, P2, P3, P4);
119-
impl_request_handler!(P1, P2, P3, P4, P5);
120-
impl_request_handler!(P1, P2, P3, P4, P5, P6);
121-
impl_request_handler!(P1, P2, P3, P4, P5, P6, P7);
122-
impl_request_handler!(P1, P2, P3, P4, P5, P6, P7, P8);
123-
impl_request_handler!(P1, P2, P3, P4, P5, P6, P7, P8, P9);
124-
impl_request_handler!(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10);
125-
126-
impl_request_handler_from_request!((P1));
127-
impl_request_handler_from_request!((P1), P2);
128-
impl_request_handler_from_request!(P1, (P2));
129-
impl_request_handler_from_request!((P1), P2, P3);
130-
impl_request_handler_from_request!(P1, (P2), P3);
131-
impl_request_handler_from_request!(P1, P2, (P3));
132-
impl_request_handler_from_request!((P1), P2, P3, P4);
133-
impl_request_handler_from_request!(P1, (P2), P3, P4);
134-
impl_request_handler_from_request!(P1, P2, (P3), P4);
135-
impl_request_handler_from_request!(P1, P2, P3, (P4));
136-
impl_request_handler_from_request!((P1), P2, P3, P4, P5);
137-
impl_request_handler_from_request!(P1, (P2), P3, P4, P5);
138-
impl_request_handler_from_request!(P1, P2, (P3), P4, P5);
139-
impl_request_handler_from_request!(P1, P2, P3, (P4), P5);
140-
impl_request_handler_from_request!(P1, P2, P3, P4, (P5));
141-
impl_request_handler_from_request!((P1), P2, P3, P4, P5, P6);
142-
impl_request_handler_from_request!(P1, (P2), P3, P4, P5, P6);
143-
impl_request_handler_from_request!(P1, P2, (P3), P4, P5, P6);
144-
impl_request_handler_from_request!(P1, P2, P3, (P4), P5, P6);
145-
impl_request_handler_from_request!(P1, P2, P3, P4, (P5), P6);
146-
impl_request_handler_from_request!(P1, P2, P3, P4, P5, (P6));
147-
impl_request_handler_from_request!((P1), P2, P3, P4, P5, P6, P7);
148-
impl_request_handler_from_request!(P1, (P2), P3, P4, P5, P6, P7);
149-
impl_request_handler_from_request!(P1, P2, (P3), P4, P5, P6, P7);
150-
impl_request_handler_from_request!(P1, P2, P3, (P4), P5, P6, P7);
151-
impl_request_handler_from_request!(P1, P2, P3, P4, (P5), P6, P7);
152-
impl_request_handler_from_request!(P1, P2, P3, P4, P5, (P6), P7);
153-
impl_request_handler_from_request!(P1, P2, P3, P4, P5, P6, (P7));
154-
impl_request_handler_from_request!((P1), P2, P3, P4, P5, P6, P7, P8);
155-
impl_request_handler_from_request!(P1, (P2), P3, P4, P5, P6, P7, P8);
156-
impl_request_handler_from_request!(P1, P2, (P3), P4, P5, P6, P7, P8);
157-
impl_request_handler_from_request!(P1, P2, P3, (P4), P5, P6, P7, P8);
158-
impl_request_handler_from_request!(P1, P2, P3, P4, (P5), P6, P7, P8);
159-
impl_request_handler_from_request!(P1, P2, P3, P4, P5, (P6), P7, P8);
160-
impl_request_handler_from_request!(P1, P2, P3, P4, P5, P6, (P7), P8);
161-
impl_request_handler_from_request!(P1, P2, P3, P4, P5, P6, P7, (P8));
162-
impl_request_handler_from_request!((P1), P2, P3, P4, P5, P6, P7, P8, P9);
163-
impl_request_handler_from_request!(P1, (P2), P3, P4, P5, P6, P7, P8, P9);
164-
impl_request_handler_from_request!(P1, P2, (P3), P4, P5, P6, P7, P8, P9);
165-
impl_request_handler_from_request!(P1, P2, P3, (P4), P5, P6, P7, P8, P9);
166-
impl_request_handler_from_request!(P1, P2, P3, P4, (P5), P6, P7, P8, P9);
167-
impl_request_handler_from_request!(P1, P2, P3, P4, P5, (P6), P7, P8, P9);
168-
impl_request_handler_from_request!(P1, P2, P3, P4, P5, P6, (P7), P8, P9);
169-
impl_request_handler_from_request!(P1, P2, P3, P4, P5, P6, P7, (P8), P9);
170-
impl_request_handler_from_request!(P1, P2, P3, P4, P5, P6, P7, P8, (P9));
171-
impl_request_handler_from_request!((P1), P2, P3, P4, P5, P6, P7, P8, P9, P10);
172-
impl_request_handler_from_request!(P1, (P2), P3, P4, P5, P6, P7, P8, P9, P10);
173-
impl_request_handler_from_request!(P1, P2, (P3), P4, P5, P6, P7, P8, P9, P10);
174-
impl_request_handler_from_request!(P1, P2, P3, (P4), P5, P6, P7, P8, P9, P10);
175-
impl_request_handler_from_request!(P1, P2, P3, P4, (P5), P6, P7, P8, P9, P10);
176-
impl_request_handler_from_request!(P1, P2, P3, P4, P5, (P6), P7, P8, P9, P10);
177-
impl_request_handler_from_request!(P1, P2, P3, P4, P5, P6, (P7), P8, P9, P10);
178-
impl_request_handler_from_request!(P1, P2, P3, P4, P5, P6, P7, (P8), P9, P10);
179-
impl_request_handler_from_request!(P1, P2, P3, P4, P5, P6, P7, P8, (P9), P10);
180-
impl_request_handler_from_request!(P1, P2, P3, P4, P5, P6, P7, P8, P9, (P10));
116+
macro_rules! handle_all_parameters {
117+
($name:ident) => {
118+
$name!();
119+
$name!(P1);
120+
$name!(P1, P2);
121+
$name!(P1, P2, P3);
122+
$name!(P1, P2, P3, P4);
123+
$name!(P1, P2, P3, P4, P5);
124+
$name!(P1, P2, P3, P4, P5, P6);
125+
$name!(P1, P2, P3, P4, P5, P6, P7);
126+
$name!(P1, P2, P3, P4, P5, P6, P7, P8);
127+
$name!(P1, P2, P3, P4, P5, P6, P7, P8, P9);
128+
$name!(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10);
129+
};
130+
}
131+
132+
macro_rules! handle_all_parameters_from_request {
133+
($name:ident) => {
134+
$name!((PX));
135+
136+
$name!((PX), P2);
137+
$name!(P1, (PX));
138+
139+
$name!((PX), P2, P3);
140+
$name!(P1, (PX), P3);
141+
$name!(P1, P2, (PX));
142+
143+
$name!((PX), P2, P3, P4);
144+
$name!(P1, (PX), P3, P4);
145+
$name!(P1, P2, (PX), P4);
146+
$name!(P1, P2, P3, (PX));
147+
148+
$name!((PX), P2, P3, P4, P5);
149+
$name!(P1, (PX), P3, P4, P5);
150+
$name!(P1, P2, (PX), P4, P5);
151+
$name!(P1, P2, P3, (PX), P5);
152+
$name!(P1, P2, P3, P4, (PX));
153+
154+
$name!((PX), P2, P3, P4, P5, P6);
155+
$name!(P1, (PX), P3, P4, P5, P6);
156+
$name!(P1, P2, (PX), P4, P5, P6);
157+
$name!(P1, P2, P3, (PX), P5, P6);
158+
$name!(P1, P2, P3, P4, (PX), P6);
159+
$name!(P1, P2, P3, P4, P5, (PX));
160+
161+
$name!((PX), P2, P3, P4, P5, P6, P7);
162+
$name!(P1, (PX), P3, P4, P5, P6, P7);
163+
$name!(P1, P2, (PX), P4, P5, P6, P7);
164+
$name!(P1, P2, P3, (PX), P5, P6, P7);
165+
$name!(P1, P2, P3, P4, (PX), P6, P7);
166+
$name!(P1, P2, P3, P4, P5, (PX), P7);
167+
$name!(P1, P2, P3, P4, P5, P6, (PX));
168+
169+
$name!((PX), P2, P3, P4, P5, P6, P7, P8);
170+
$name!(P1, (PX), P3, P4, P5, P6, P7, P8);
171+
$name!(P1, P2, (PX), P4, P5, P6, P7, P8);
172+
$name!(P1, P2, P3, (PX), P5, P6, P7, P8);
173+
$name!(P1, P2, P3, P4, (PX), P6, P7, P8);
174+
$name!(P1, P2, P3, P4, P5, (PX), P7, P8);
175+
$name!(P1, P2, P3, P4, P5, P6, (PX), P8);
176+
$name!(P1, P2, P3, P4, P5, P6, P7, (PX));
177+
178+
$name!((PX), P2, P3, P4, P5, P6, P7, P8, P9);
179+
$name!(P1, (PX), P3, P4, P5, P6, P7, P8, P9);
180+
$name!(P1, P2, (PX), P4, P5, P6, P7, P8, P9);
181+
$name!(P1, P2, P3, (PX), P5, P6, P7, P8, P9);
182+
$name!(P1, P2, P3, P4, (PX), P6, P7, P8, P9);
183+
$name!(P1, P2, P3, P4, P5, (PX), P7, P8, P9);
184+
$name!(P1, P2, P3, P4, P5, P6, (PX), P8, P9);
185+
$name!(P1, P2, P3, P4, P5, P6, P7, (PX), P9);
186+
$name!(P1, P2, P3, P4, P5, P6, P7, P8, (PX));
187+
188+
$name!((PX), P2, P3, P4, P5, P6, P7, P8, P9, P10);
189+
$name!(P1, (PX), P3, P4, P5, P6, P7, P8, P9, P10);
190+
$name!(P1, P2, (PX), P4, P5, P6, P7, P8, P9, P10);
191+
$name!(P1, P2, P3, (PX), P5, P6, P7, P8, P9, P10);
192+
$name!(P1, P2, P3, P4, (PX), P6, P7, P8, P9, P10);
193+
$name!(P1, P2, P3, P4, P5, (PX), P7, P8, P9, P10);
194+
$name!(P1, P2, P3, P4, P5, P6, (PX), P8, P9, P10);
195+
$name!(P1, P2, P3, P4, P5, P6, P7, (PX), P9, P10);
196+
$name!(P1, P2, P3, P4, P5, P6, P7, P8, (PX), P10);
197+
$name!(P1, P2, P3, P4, P5, P6, P7, P8, P9, (P10));
198+
};
199+
}
200+
201+
handle_all_parameters!(impl_request_handler);
202+
handle_all_parameters_from_request!(impl_request_handler_from_request);
181203

182204
/// A wrapper around a handler that's used in
183205
/// [`Bootstrapper`](cot::Bootstrapper).

cot/src/headers.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ pub(crate) const HTML_CONTENT_TYPE: &str = "text/html; charset=utf-8";
22
pub(crate) const FORM_CONTENT_TYPE: &str = "application/x-www-form-urlencoded";
33
#[cfg(feature = "json")]
44
pub(crate) const JSON_CONTENT_TYPE: &str = "application/json";
5+
pub(crate) const PLAIN_TEXT_CONTENT_TYPE: &str = "text/plain; charset=utf-8";
6+
pub(crate) const OCTET_STREAM_CONTENT_TYPE: &str = "application/octet-stream";

cot/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ pub mod cli;
6969
pub mod common_types;
7070
pub mod config;
7171
mod error_page;
72-
mod handler;
72+
#[macro_use]
73+
pub(crate) mod handler;
7374
pub mod html;
7475
pub mod middleware;
7576
#[cfg(feature = "openapi")]

cot/src/openapi.rs

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -543,17 +543,7 @@ macro_rules! impl_as_openapi_operation {
543543
};
544544
}
545545

546-
impl_as_openapi_operation!();
547-
impl_as_openapi_operation!(P1);
548-
impl_as_openapi_operation!(P1, P2);
549-
impl_as_openapi_operation!(P1, P2, P3);
550-
impl_as_openapi_operation!(P1, P2, P3, P4);
551-
impl_as_openapi_operation!(P1, P2, P3, P4, P5);
552-
impl_as_openapi_operation!(P1, P2, P3, P4, P5, P6);
553-
impl_as_openapi_operation!(P1, P2, P3, P4, P5, P6, P7);
554-
impl_as_openapi_operation!(P1, P2, P3, P4, P5, P6, P7, P8);
555-
impl_as_openapi_operation!(P1, P2, P3, P4, P5, P6, P7, P8, P9);
556-
impl_as_openapi_operation!(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10);
546+
handle_all_parameters!(impl_as_openapi_operation);
557547

558548
/// A trait that can be implemented for types that should be taken into
559549
/// account when generating OpenAPI paths.
@@ -884,10 +874,9 @@ mod tests {
884874
use serde::{Deserialize, Serialize};
885875

886876
use super::*;
877+
use crate::html::Html;
887878
use crate::openapi::AsApiOperation;
888879
use crate::request::extractors::{Json, Path, UrlQuery};
889-
use crate::response::{Response, ResponseExt};
890-
use crate::{Body, StatusCode};
891880

892881
#[derive(Deserialize, Serialize, schemars::JsonSchema)]
893882
struct TestRequest {
@@ -910,8 +899,8 @@ mod tests {
910899
field2: i32,
911900
}
912901

913-
async fn test_handler() -> cot::Result<Response> {
914-
Ok(Response::new_html(StatusCode::OK, Body::fixed("test")))
902+
async fn test_handler() -> Html {
903+
Html::new("test")
915904
}
916905

917906
#[test]

0 commit comments

Comments
 (0)