Skip to main content

anvil_core/
response.rs

1//! Response builders. View rendering, redirects, JSON helpers.
2
3use axum::http::{HeaderMap, HeaderValue, StatusCode};
4use axum::response::{IntoResponse, Response};
5use serde::Serialize;
6
7/// A response that carries a Forge-rendered view body.
8pub struct ViewResponse {
9    pub status: StatusCode,
10    pub body: String,
11    pub headers: HeaderMap,
12}
13
14impl ViewResponse {
15    pub fn new(body: impl Into<String>) -> Self {
16        let mut headers = HeaderMap::new();
17        headers.insert(
18            axum::http::header::CONTENT_TYPE,
19            HeaderValue::from_static("text/html; charset=utf-8"),
20        );
21        Self {
22            status: StatusCode::OK,
23            body: body.into(),
24            headers,
25        }
26    }
27
28    pub fn status(mut self, s: StatusCode) -> Self {
29        self.status = s;
30        self
31    }
32}
33
34impl IntoResponse for ViewResponse {
35    fn into_response(self) -> Response {
36        (self.status, self.headers, self.body).into_response()
37    }
38}
39
40/// A redirect response.
41pub struct Redirect {
42    pub status: StatusCode,
43    pub location: String,
44    pub flash: Option<(String, String)>,
45}
46
47impl Redirect {
48    pub fn to(location: impl Into<String>) -> Self {
49        Self {
50            status: StatusCode::SEE_OTHER,
51            location: location.into(),
52            flash: None,
53        }
54    }
55
56    pub fn back() -> Self {
57        Self::to("/")
58    }
59
60    pub fn permanent(location: impl Into<String>) -> Self {
61        Self {
62            status: StatusCode::MOVED_PERMANENTLY,
63            location: location.into(),
64            flash: None,
65        }
66    }
67
68    pub fn with(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
69        self.flash = Some((key.into(), value.into()));
70        self
71    }
72}
73
74impl IntoResponse for Redirect {
75    fn into_response(self) -> Response {
76        let mut headers = HeaderMap::new();
77        headers.insert(
78            axum::http::header::LOCATION,
79            HeaderValue::from_str(&self.location).unwrap_or_else(|_| HeaderValue::from_static("/")),
80        );
81        (self.status, headers).into_response()
82    }
83}
84
85/// Trait for "smart" responders — types that know how to render themselves.
86/// Implemented for the common types so handlers can `Ok(json(...))` etc.
87pub trait Responder {
88    fn respond(self) -> Response;
89}
90
91impl<T: IntoResponse> Responder for T {
92    fn respond(self) -> Response {
93        self.into_response()
94    }
95}
96
97pub fn json<T: Serialize>(value: T) -> axum::Json<T> {
98    axum::Json(value)
99}
100
101pub fn no_content() -> Response {
102    (StatusCode::NO_CONTENT).into_response()
103}