1#![forbid(unsafe_code)]
5#![deny(
6 missing_copy_implementations,
7 rustdoc::missing_crate_level_docs,
8 missing_debug_implementations,
9 missing_docs,
10 nonstandard_style,
11 unused_qualifications
12)]
13
14use std::borrow::Cow;
15use trillium::{Conn, Handler, KnownHeaderName::Location, Status};
16
17#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
21pub enum RedirectStatus {
22 MultipleChoices,
24 MovedPermanently,
26 #[default]
28 Found,
29 SeeOther,
31 TemporaryRedirect,
33 PermanentRedirect,
35}
36
37impl From<RedirectStatus> for Status {
38 fn from(value: RedirectStatus) -> Self {
39 match value {
40 RedirectStatus::MultipleChoices => Status::MultipleChoice,
41 RedirectStatus::MovedPermanently => Status::MovedPermanently,
42 RedirectStatus::Found => Status::Found,
43 RedirectStatus::SeeOther => Status::SeeOther,
44 RedirectStatus::TemporaryRedirect => Status::TemporaryRedirect,
45 RedirectStatus::PermanentRedirect => Status::PermanentRedirect,
46 }
47 }
48}
49
50#[derive(Clone, Debug)]
52pub struct Redirect {
53 to: Cow<'static, str>,
54 status: RedirectStatus,
55}
56
57impl Redirect {
58 pub fn to(to: impl Into<Cow<'static, str>>) -> Self {
60 Self {
61 to: to.into(),
62 status: RedirectStatus::default(),
63 }
64 }
65
66 pub fn with_redirect_status(mut self, status: RedirectStatus) -> Self {
68 self.status = status;
69 self
70 }
71}
72
73pub fn redirect(to: impl Into<Cow<'static, str>>) -> Redirect {
75 Redirect::to(to)
76}
77
78#[trillium::async_trait]
79impl Handler for Redirect {
80 async fn run(&self, conn: Conn) -> Conn {
81 conn.redirect_as(self.to.clone(), self.status)
82 }
83}
84
85pub trait RedirectConnExt {
87 fn redirect(self, to: impl Into<Cow<'static, str>>) -> Self;
89 fn redirect_as(self, to: impl Into<Cow<'static, str>>, status: RedirectStatus) -> Self;
91}
92
93impl RedirectConnExt for Conn {
94 fn redirect(self, to: impl Into<Cow<'static, str>>) -> Self {
95 self.redirect_as(to, RedirectStatus::default())
96 }
97
98 fn redirect_as(self, to: impl Into<Cow<'static, str>>, status: RedirectStatus) -> Self {
99 self.with_status(status)
100 .with_response_header(Location, to.into())
101 .halt()
102 }
103}