Skip to main content

trillium_redirect/
lib.rs

1//! Trillium handler for redirection
2#![forbid(unsafe_code)]
3#![deny(
4    missing_copy_implementations,
5    rustdoc::missing_crate_level_docs,
6    missing_debug_implementations,
7    missing_docs,
8    nonstandard_style,
9    unused_qualifications
10)]
11#![cfg_attr(docsrs, feature(doc_cfg))]
12
13#[cfg(test)]
14#[doc = include_str!("../README.md")]
15mod readme {}
16
17#[cfg(feature = "client")]
18#[cfg_attr(docsrs, doc(cfg(feature = "client")))]
19pub mod client;
20
21use std::borrow::Cow;
22use trillium::{Conn, Handler, KnownHeaderName::Location, Status};
23
24/// The subset of http statuses that indicate redirection
25///
26/// The default is [`RedirectStatus::Found`]
27#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
28pub enum RedirectStatus {
29    /// [300 Multiple Choices](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/300)
30    MultipleChoices,
31    /// [301 Moved Permanently](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/301)
32    MovedPermanently,
33    /// [302 Found](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302)
34    #[default]
35    Found,
36    /// [303 See Other](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/303)
37    SeeOther,
38    /// [307 Temporary Redirect](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307)
39    TemporaryRedirect,
40    /// [308 Permanent Redirect](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308)
41    PermanentRedirect,
42}
43
44impl From<RedirectStatus> for Status {
45    fn from(value: RedirectStatus) -> Self {
46        match value {
47            RedirectStatus::MultipleChoices => Status::MultipleChoice,
48            RedirectStatus::MovedPermanently => Status::MovedPermanently,
49            RedirectStatus::Found => Status::Found,
50            RedirectStatus::SeeOther => Status::SeeOther,
51            RedirectStatus::TemporaryRedirect => Status::TemporaryRedirect,
52            RedirectStatus::PermanentRedirect => Status::PermanentRedirect,
53        }
54    }
55}
56
57/// A simple handler for redirection
58#[derive(Clone, Debug)]
59pub struct Redirect {
60    to: Cow<'static, str>,
61    status: RedirectStatus,
62}
63
64impl Redirect {
65    /// Redirect to the provided path or url with the default redirect status
66    pub fn to(to: impl Into<Cow<'static, str>>) -> Self {
67        Self {
68            to: to.into(),
69            status: RedirectStatus::default(),
70        }
71    }
72
73    /// Provide a [`RedirectStatus`] for this redirect handler
74    pub fn with_redirect_status(mut self, status: RedirectStatus) -> Self {
75        self.status = status;
76        self
77    }
78}
79
80/// Redirect to the provided path or url with the default redirect status
81pub fn redirect(to: impl Into<Cow<'static, str>>) -> Redirect {
82    Redirect::to(to)
83}
84
85impl Handler for Redirect {
86    async fn run(&self, conn: Conn) -> Conn {
87        conn.redirect_as(self.to.clone(), self.status)
88    }
89}
90
91/// An extension trait for [`trillium::Conn`] for redirection
92pub trait RedirectConnExt {
93    /// redirect this conn with the default redirect status
94    fn redirect(self, to: impl Into<Cow<'static, str>>) -> Self;
95    /// redirect this conn with the provided redirect status
96    fn redirect_as(self, to: impl Into<Cow<'static, str>>, status: RedirectStatus) -> Self;
97}
98
99impl RedirectConnExt for Conn {
100    fn redirect(self, to: impl Into<Cow<'static, str>>) -> Self {
101        self.redirect_as(to, RedirectStatus::default())
102    }
103
104    fn redirect_as(self, to: impl Into<Cow<'static, str>>, status: RedirectStatus) -> Self {
105        self.with_status(status)
106            .with_response_header(Location, to.into())
107            .halt()
108    }
109}