Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 62 additions & 2 deletions cot/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ pub trait ResponseExt: Sized + private::Sealed {
/// Create a new redirect response.
///
/// This creates a new [`Response`] object with a status code of
/// [`StatusCode::SEE_OTHER`] and a location header set to the provided
/// location.
/// [`StatusCode::SEE_OTHER`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/303)
/// and a location header set to the provided location.
///
/// # Examples
///
Expand All @@ -125,6 +125,7 @@ pub trait ResponseExt: Sized + private::Sealed {
/// * [`crate::reverse_redirect!`] – a more ergonomic way to create
/// redirects to internal views
#[must_use]
#[deprecated(since = "0.5.0", note = "Use Redirect::new() instead")]
fn new_redirect<T: Into<String>>(location: T) -> Self;
}

Expand All @@ -144,6 +145,53 @@ impl ResponseExt for Response {
}
}

/// A redirect response.
///
/// This type creates an HTTP redirect response with a status code of
/// [`StatusCode::SEE_OTHER`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/303)
/// (303) and a `Location` header set to the specified URL.
///
/// # Examples
///
/// ```
/// use cot::response::{IntoResponse, Redirect};
///
/// let redirect = Redirect::new("https://example.com");
/// let response = redirect.into_response().unwrap();
///
/// assert_eq!(response.status(), cot::StatusCode::SEE_OTHER);
/// ```
///
/// # See also
///
/// * [`crate::reverse_redirect!`] – a more ergonomic way to create redirects to
/// internal views
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Redirect(String);

impl Redirect {
/// Creates a new redirect response to the specified location.
///
/// Creates an HTTP redirect response with a status code of
/// [`StatusCode::SEE_OTHER`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/303)
/// (303) and a `Location` header set to the specified URL.
///
/// # Examples
///
/// ```
/// use cot::response::{IntoResponse, Redirect};
///
/// let redirect = Redirect::new("https://example.com");
/// let response = redirect.into_response().unwrap();
///
/// assert_eq!(response.status(), cot::StatusCode::SEE_OTHER);
/// ```
#[must_use]
pub fn new<T: Into<String>>(location: T) -> Self {
Self(location.into())
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -179,6 +227,7 @@ mod tests {
}

#[test]
#[expect(deprecated)]
fn response_new_redirect() {
let location = "http://example.com";
let response = Response::new_redirect(location);
Expand All @@ -188,4 +237,15 @@ mod tests {
location
);
}

#[test]
fn response_new_redirect_struct() {
let location = "http://example.com";
let response = Redirect::new(location).into_response().unwrap();
assert_eq!(response.status(), StatusCode::SEE_OTHER);
assert_eq!(
response.headers().get(http::header::LOCATION).unwrap(),
location
);
}
}
14 changes: 13 additions & 1 deletion cot/src/response/into_response.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use bytes::{Bytes, BytesMut};
use cot::error::error_impl::impl_into_cot_error;
use cot::headers::{HTML_CONTENT_TYPE, OCTET_STREAM_CONTENT_TYPE, PLAIN_TEXT_CONTENT_TYPE};
use cot::response::Response;
use cot::response::{RESPONSE_BUILD_FAILURE, Response};
use cot::{Body, Error, StatusCode};
use http;

#[cfg(feature = "json")]
use crate::headers::JSON_CONTENT_TYPE;
use crate::html::Html;
use crate::response::Redirect;

/// Trait for generating responses.
/// Types that implement `IntoResponse` can be returned from handlers.
Expand Down Expand Up @@ -385,6 +386,17 @@ impl IntoResponse for Body {
}
}

impl IntoResponse for Redirect {
fn into_response(self) -> cot::Result<Response> {
let response = http::Response::builder()
.status(StatusCode::SEE_OTHER)
.header(http::header::LOCATION, self.0)
.body(Body::empty())
.expect(RESPONSE_BUILD_FAILURE);
Ok(response)
}
}

#[cfg(test)]
mod tests {
use bytes::{Bytes, BytesMut};
Expand Down
5 changes: 4 additions & 1 deletion cot/src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1022,7 +1022,10 @@ macro_rules! reverse_redirect {
$request,
$view_name,
$( $($key = $value),* )?
).map(|url| <$crate::response::Response as $crate::response::ResponseExt>::new_redirect(url))
).map(|url|
$crate::response::IntoResponse::into_response($crate::response::Redirect::new(url))
.expect("Failed to build response")
)
};
}

Expand Down
Loading