Add custom response type (#27)

* feat: support route names with dots

* feat: add support for Response::Custom value

* feat: statically export xml files
This commit is contained in:
Valerio Ageno
2024-08-18 11:09:27 +02:00
committed by GitHub
parent 3636268a71
commit acd663d79f
4 changed files with 40 additions and 14 deletions
+2
View File
@@ -120,6 +120,7 @@ mod tests {
"/home/user/Documents/tuono/src/routes/posts/index.rs",
"/home/user/Documents/tuono/src/routes/posts/[post].rs",
"/home/user/Documents/tuono/src/routes/posts/UPPERCASE.rs",
"/home/user/Documents/tuono/src/routes/sitemap.xml.rs",
];
routes
@@ -132,6 +133,7 @@ mod tests {
("/posts/index", "posts_index"),
("/posts/[post]", "posts_dyn_post"),
("/posts/UPPERCASE", "posts_uppercase"),
("/sitemap.xml", "sitemap_dot_xml"),
];
results.into_iter().for_each(|(path, module_import)| {
+1 -1
View File
@@ -105,7 +105,7 @@ pub fn app() -> std::io::Result<()> {
}
for (_, route) in app.route_map {
route.save_ssg_html(&reqwest)
route.save_ssg_file(&reqwest)
}
// Close server
+25 -6
View File
@@ -27,9 +27,16 @@ impl AxumInfo {
let axum_route = path.replace("/index", "");
let module_import = module
.as_str()
.to_string()
.replace('/', "_")
.replace('.', "_dot_")
.to_lowercase();
if axum_route.is_empty() {
return AxumInfo {
module_import: module.as_str().to_string().replace('/', "_"),
module_import,
axum_route: "/".to_string(),
};
}
@@ -47,12 +54,15 @@ impl AxumInfo {
}
AxumInfo {
module_import: module.as_str().to_string().replace('/', "_").to_lowercase(),
module_import,
axum_route,
}
}
}
// TODO: to be extended with common scenarios
const NO_HTML_EXTENSIONS: [&str; 1] = ["xml"];
#[derive(Debug, PartialEq, Eq)]
pub struct Route {
path: String,
@@ -73,7 +83,7 @@ impl Route {
self.axum_info = Some(AxumInfo::new(self.path.clone()))
}
pub fn save_ssg_html(&self, reqwest: &Client) {
pub fn save_ssg_file(&self, reqwest: &Client) {
let path = &self.path.replace("index", "");
let mut response = reqwest
@@ -81,7 +91,7 @@ impl Route {
.send()
.unwrap();
let file_path = self.html_file_path();
let file_path = self.output_file_path();
let parent_dir = file_path.parent().unwrap();
@@ -124,8 +134,16 @@ impl Route {
}
}
fn html_file_path(&self) -> PathBuf {
fn output_file_path(&self) -> PathBuf {
let cleaned_path = self.path.replace("index", "");
if NO_HTML_EXTENSIONS
.iter()
.any(|extension| self.path.ends_with(extension))
{
return PathBuf::from(format!("out/static{}", cleaned_path));
}
PathBuf::from(format!("out/static{}/index.html", cleaned_path))
}
}
@@ -173,6 +191,7 @@ mod tests {
let routes = [
("/index", "out/static/index.html"),
("/documentation", "out/static/documentation/index.html"),
("/sitemap.xml", "out/static/sitemap.xml"),
(
"/documentation/routing",
"out/static/documentation/routing/index.html",
@@ -182,7 +201,7 @@ mod tests {
for (path, html) in routes {
let route = Route::new(path.to_string());
assert_eq!(route.html_file_path(), PathBuf::from(html))
assert_eq!(route.output_file_path(), PathBuf::from(html))
}
}
}
+12 -7
View File
@@ -1,7 +1,7 @@
use crate::Request;
use crate::{ssr::Js, Payload};
use axum::http::StatusCode;
use axum::response::{Html, IntoResponse, Redirect, Response as AxumResponse};
use axum::http::{HeaderMap, StatusCode};
use axum::response::{Html, IntoResponse, Redirect};
use axum::Json;
use erased_serde::Serialize;
@@ -13,6 +13,8 @@ pub struct Props {
pub enum Response {
Redirect(String),
Props(Props),
// TODO: improve this tuple to support a more generic IntoResponse
Custom((StatusCode, HeaderMap, String)),
}
#[derive(serde::Serialize)]
@@ -67,19 +69,19 @@ impl Props {
}
impl Response {
pub fn render_to_string(&self, req: Request) -> AxumResponse {
pub fn render_to_string(&self, req: Request) -> impl IntoResponse {
match self {
Self::Props(Props { data, http_code }) => {
let payload = Payload::new(&req, data).client_payload().unwrap();
match Js::render_to_string(Some(&payload)) {
Ok(html) => (*http_code, Html(html)).into_response(),
Err(_) => {
(*http_code, Html("500 Internal server error".to_string())).into_response()
}
Ok(html) => (*http_code, Html(html)),
Err(_) => (*http_code, Html("500 Internal server error".to_string())),
}
.into_response()
}
Self::Redirect(to) => Redirect::permanent(to).into_response(),
Self::Custom(response) => response.clone().into_response(),
}
}
@@ -93,6 +95,9 @@ impl Response {
Json(JsonResponse::new_redirect(destination.to_string())),
)
.into_response(),
// Custom never needs the "data" response since its scope
// is outside the react domain
Self::Custom(_) => (StatusCode::OK, Json("{}")).into_response(),
}
}
}