#![forbid(unsafe_code)]
#![deny(
missing_copy_implementations,
rustdoc::missing_crate_level_docs,
missing_debug_implementations,
missing_docs,
nonstandard_style,
unused_qualifications
)]
use std::borrow::Cow;
use trillium::{Conn, Handler, KnownHeaderName::Location, Status};
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
pub enum RedirectStatus {
MultipleChoices,
MovedPermanently,
#[default]
Found,
SeeOther,
TemporaryRedirect,
PermanentRedirect,
}
impl From<RedirectStatus> for Status {
fn from(value: RedirectStatus) -> Self {
match value {
RedirectStatus::MultipleChoices => Status::MultipleChoice,
RedirectStatus::MovedPermanently => Status::MovedPermanently,
RedirectStatus::Found => Status::Found,
RedirectStatus::SeeOther => Status::SeeOther,
RedirectStatus::TemporaryRedirect => Status::TemporaryRedirect,
RedirectStatus::PermanentRedirect => Status::PermanentRedirect,
}
}
}
#[derive(Clone, Debug)]
pub struct Redirect {
to: Cow<'static, str>,
status: RedirectStatus,
}
impl Redirect {
pub fn to(to: impl Into<Cow<'static, str>>) -> Self {
Self {
to: to.into(),
status: RedirectStatus::default(),
}
}
pub fn with_redirect_status(mut self, status: RedirectStatus) -> Self {
self.status = status;
self
}
}
pub fn redirect(to: impl Into<Cow<'static, str>>) -> Redirect {
Redirect::to(to)
}
#[trillium::async_trait]
impl Handler for Redirect {
async fn run(&self, conn: Conn) -> Conn {
conn.redirect_as(self.to.clone(), self.status)
}
}
pub trait RedirectConnExt {
fn redirect(self, to: impl Into<Cow<'static, str>>) -> Self;
fn redirect_as(self, to: impl Into<Cow<'static, str>>, status: RedirectStatus) -> Self;
}
impl RedirectConnExt for Conn {
fn redirect(self, to: impl Into<Cow<'static, str>>) -> Self {
self.redirect_as(to, RedirectStatus::default())
}
fn redirect_as(self, to: impl Into<Cow<'static, str>>, status: RedirectStatus) -> Self {
self.with_status(status)
.with_response_header(Location, to.into())
.halt()
}
}