use axum::body::Body;
use axum::http::{HeaderMap, HeaderName, HeaderValue, StatusCode};
use axum::response::{IntoResponse, Response};
use super::error::InertiaError;
use super::headers::Headers;
#[derive(Debug, Clone)]
pub enum Redirect {
Standard {
location: String,
method: axum::http::Method,
},
External { location: String },
Fragment { location: String },
}
impl Redirect {
pub fn to(location: impl Into<String>, method: axum::http::Method) -> Self {
Redirect::Standard {
location: location.into(),
method,
}
}
pub fn build(self) -> Result<Response, InertiaError> {
Ok(match self {
Redirect::Standard { location, method } => {
let status = if matches!(
method,
axum::http::Method::PUT
| axum::http::Method::PATCH
| axum::http::Method::DELETE
) {
StatusCode::SEE_OTHER
} else {
StatusCode::FOUND
};
standard_response(&location, status)?
}
Redirect::External { location } => control_response(&location, Headers::LOCATION)?,
Redirect::Fragment { location } => control_response(&location, Headers::REDIRECT)?,
})
}
}
impl IntoResponse for Redirect {
fn into_response(self) -> Response {
match self.build() {
Ok(r) => r,
Err(e) => e.into_response(),
}
}
}
fn standard_response(location: &str, status: StatusCode) -> Result<Response, InertiaError> {
let value = HeaderValue::from_str(location)
.map_err(axum::http::Error::from)
.map_err(InertiaError::Location)?;
let mut headers = HeaderMap::new();
headers.insert(axum::http::header::LOCATION, value);
Ok((status, headers, Body::empty()).into_response())
}
fn control_response(location: &str, header: HeaderName) -> Result<Response, InertiaError> {
let value = HeaderValue::from_str(location)
.map_err(axum::http::Error::from)
.map_err(InertiaError::Location)?;
let mut headers = HeaderMap::new();
headers.insert(header, value);
Ok((StatusCode::CONFLICT, headers, Body::empty()).into_response())
}
pub fn redirect(location: impl Into<String>, method: axum::http::Method) -> Redirect {
Redirect::to(location, method)
}
pub fn external(location: impl Into<String>) -> Redirect {
Redirect::External {
location: location.into(),
}
}
pub fn fragment(location: impl Into<String>) -> Redirect {
Redirect::Fragment {
location: location.into(),
}
}