lib_humus/view.rs
1// SPDX-FileCopyrightText: 2026 Slatian <baschdel@disroot.org>
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5use axum::http::status::StatusCode;
6use axum::response::IntoResponse;
7use axum::response::Json;
8use axum::response::Response;
9use serde::Serialize;
10use tera::Context;
11
12use crate::HumusQuerySettings;
13use crate::templating::HumusApiFormat;
14
15/// π Provides data and logic for the [HumusEngine] and
16/// knows how to put information together.
17///
18/// It is recommended to implement this as an enum carrying additional information.
19///
20/// π‘Also have a look at the provided methods, while they may be sane defaults
21/// they are probably not always the desired behavior.
22///
23/// [HumusEngine]: ./struct.HumusEngine.html
24pub trait HumusView<S, ApiFormat>: Serialize + Sized
25where
26 S: HumusQuerySettings,
27 ApiFormat: HumusApiFormat,
28{
29 /// Returns the template name that will be used to select
30 /// the template file.
31 ///
32 /// If the name is "404" for an html response the template
33 /// file "404.html" will be used.
34 ///
35 /// Also ends up as the `view` variable in the template.
36 /// Example:
37 /// ```rust,ignore
38 /// fn get_template_name(&self) -> String {
39 /// match self {
40 /// Self::Index{..} => "index",
41 /// Self::Results{..} => "results",
42 /// Self::NotFound => "404",
43 /// Self::InternalError{..} => "500",
44 /// }.to_string()
45 /// }
46 /// ```
47 fn get_template_name(&self) -> String;
48
49 /// Returns the reponse code for the view.
50 ///
51 /// The numeric value will be useable as `http_status` in the template.
52 ///
53 /// Example:
54 /// ```rust,ignore
55 /// use axum::http::StatusCode;
56 ///
57 /// fn get_status_code(&self, settings: &SomeSettings) -> StatusCode {
58 /// match self {
59 /// Self::NotFound => StatusCode::NOT_FOUND,
60 /// Self::InternalError{..} => StatusCode::INTERNAL_SERVER_ERROR,
61 /// _ => StatusCode::OK,
62 /// }
63 /// }
64 /// ```
65 fn get_status_code(&self, settings: &S) -> StatusCode;
66
67 /// [Hook called before rendering a template][crate::templating::TemplatingEngine::render_view]
68 /// to initalize it with additional values.
69 ///
70 /// π Remember to document which values you set here in a place someone who
71 /// wants to do something with templating is able to find it.
72 ///
73 /// The default implementation does nothing.
74 fn initalize_template_context(&self, _context: &mut Context, _settings: &S) {}
75
76 /// [Hook to update non-API responses][crate::templating::TemplatingEngine::render_view]
77 /// after they have been built.
78 ///
79 /// **Note:** The default implementation of [Self::into_api_response] does call this method, when implementing a custom version of it it is your decision wheter to call it or not.
80 ///
81 /// When migrating from earlier versions set your cookie headers in here.
82 /// See also [axum: Constructing a Cookie][axum_extra::extract::cookie::Cookie#constructing-a-cookie]
83 ///
84 /// Useful for setting extra headers. Does nothing by default.
85 fn update_response(&self, response: Response, _settings: &S) -> Response {
86 response
87 }
88
89 /// Return an API-Response
90 ///
91 /// By default causes the view to Serialize itself
92 /// to a json response using serde.
93 ///
94 /// Response code and cookie headers are queried in
95 /// advance and set on the reulting response if it has a status code of 200.
96 /// Otherwise it is assumed that the response generating logic
97 /// alredy took care of that.
98 ///
99 /// You'll need the following imports when implementing:
100 /// ```
101 /// use axum::Json;
102 /// use axum::response::IntoResponse;
103 /// ```
104 ///
105 /// The api format in the seconds argument is guaranteed to be derived from the settings given in the first argument.
106 fn into_api_response(self, settings: &S, _api_format: ApiFormat) -> Response {
107 let response = Json(&self).into_response();
108 self.update_response(response, settings)
109 }
110}