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