trillium_redirect/
lib.rs

1/*!
2Trillium handler for redirection
3*/
4#![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/// The subset of http statuses that indicate redirection
18///
19/// The default is [`RedirectStatus::Found`]
20#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
21pub enum RedirectStatus {
22    /// [300 Multiple Choices](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/300)
23    MultipleChoices,
24    /// [301 Moved Permanently](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/301)
25    MovedPermanently,
26    /// [302 Found](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302)
27    #[default]
28    Found,
29    /// [303 See Other](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/303)
30    SeeOther,
31    /// [307 Temporary Redirect](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307)
32    TemporaryRedirect,
33    /// [308 Permanent Redirect](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308)
34    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/// A simple handler for redirection
51#[derive(Clone, Debug)]
52pub struct Redirect {
53    to: Cow<'static, str>,
54    status: RedirectStatus,
55}
56
57impl Redirect {
58    /// Redirect to the provided path or url with the default redirect status
59    pub fn to(to: impl Into<Cow<'static, str>>) -> Self {
60        Self {
61            to: to.into(),
62            status: RedirectStatus::default(),
63        }
64    }
65
66    /// Provide a [`RedirectStatus`] for this redirect handler
67    pub fn with_redirect_status(mut self, status: RedirectStatus) -> Self {
68        self.status = status;
69        self
70    }
71}
72
73/// Redirect to the provided path or url with the default redirect status
74pub 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
85/// An extension trait for [`trillium::Conn`] for redirection
86pub trait RedirectConnExt {
87    /// redirect this conn with the default redirect status
88    fn redirect(self, to: impl Into<Cow<'static, str>>) -> Self;
89    /// redirect this conn with the provided redirect status
90    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}