mirror of
https://github.com/tuono-labs/tuono
synced 2026-07-25 12:52:47 -07:00
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:
@@ -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)| {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user