From e34b4b321edc137d6412aaf7c1685669e4ad65b8 Mon Sep 17 00:00:00 2001 From: Ross Andrews Date: Mon, 1 Sep 2025 19:15:30 -0500 Subject: [PATCH] checkpoint --- Cargo.lock | 1 + Cargo.toml | 1 + src/renderer.rs | 35 +++++++++++++++++++++++++++-------- src/server.rs | 37 ++++++++++++++++++++++--------------- templates/404.html | 9 +++++++++ 5 files changed, 60 insertions(+), 23 deletions(-) create mode 100644 templates/404.html diff --git a/Cargo.lock b/Cargo.lock index 3b6b265..d5beb67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1133,6 +1133,7 @@ name = "fleen" version = "0.1.0" dependencies = [ "axum", + "bytes", "eframe", "egui_ltreeview", "markdown", diff --git a/Cargo.toml b/Cargo.toml index e50e84a..e80cc48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,4 @@ markdown = "1.0.0" serde = { version = "1.0.219", features = ["derive"] } axum = "0.8.4" tokio = { version = "1.47.1", features = ["full"] } +bytes = "1.10.1" diff --git a/src/renderer.rs b/src/renderer.rs index 629c09b..0aaa43f 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -71,15 +71,28 @@ pub fn default_path(root: &PathBuf) -> PathBuf { /// Take a source file path (relative to the root) and the root path, and return a RenderOutput for it pub fn render(source: PathBuf, root: &PathBuf) -> Result { + let extension = source.extension().map(|o| o.to_str().unwrap()); if skipped_path(source.clone()) { - return Ok(RenderOutput::NoOutput) - } - if root.join(source.clone()).is_dir() { - return Ok(RenderOutput::Dir(source)) - } - match source.extension().map(|o| o.to_str().unwrap()) { - Some("md") => render_as_markdown(source.clone(), root), - _ => Ok(RenderOutput::RawFile(source)) + // Skipped path, nothing + Ok(RenderOutput::NoOutput) + } else if root.join(source.clone()).is_dir() { + // Dir, which matters for producing files + Ok(RenderOutput::Dir(source)) + } else if let Ok(true) = fs::exists(root.join(source.clone())) { + match extension { + // Asked for a markdown file, render it + // TODO this is a little silly for the server, this should properly be NoOutput but that's a big change + Some("md") => render_as_markdown(source.clone(), root), + // Not a markdown file, but it exists, return it raw + _ => Ok(RenderOutput::RawFile(root.join(source))) + } + } else if matches!(extension, Some("html")) && + let Ok(true) = fs::exists(root.join(source.with_extension("md"))) { + // We asked for an html file which doesn't exist but a corresponding md file does, render it + render_as_markdown(source.with_extension("md"), root) + } else { + // Asked for something which doesn't exist and it's not the md -> html case, 404: + Ok(RenderOutput::NoOutput) } } @@ -208,4 +221,10 @@ mod tests { fn test_dir() { assert!(matches!(render_file("dir"), RenderOutput::Dir(_))); } + + #[test] + fn test_ask_for_html() { + let contents = render_file("index.html"); + assert!(matches!(contents, RenderOutput::Rendered(_, _))) // We asked for the html file which doesn't exist but the md does + } } \ No newline at end of file diff --git a/src/server.rs b/src/server.rs index b26eeed..7e37671 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,6 +1,9 @@ +use std::fs; use std::path::PathBuf; use std::sync::Arc; +use axum::body::Body; use axum::extract::State; +use axum::http::Uri; use axum::response::{IntoResponse, Response}; use axum::Router; use axum::routing::get; @@ -9,7 +12,13 @@ use crate::renderer::{default_path, render, RenderOutput}; pub async fn start_server(root: PathBuf) { let state = Arc::new(root); let app = Router::new() - .route("/", get(handler)) + .route("/", get(|State(root): State>| async { + let path = default_path(&root); + serve_path(String::from(path.to_str().unwrap()), root) + })) + .route("/{*path}", get(|State(root): State>, uri: Uri| async move { + serve_path(String::from(uri.path()), root) + })) .with_state(state); // run our app with hyper, listening globally on port 3000 @@ -17,34 +26,32 @@ pub async fn start_server(root: PathBuf) { axum::serve(listener, app).await.unwrap(); } -async fn handler( - State(root): State>, -) -> impl IntoResponse { - let path = default_path(&root); - serve_path(String::from(path.to_str().unwrap()), root) -} - fn serve_path(path: String, root: impl AsRef) -> impl IntoResponse { + let path = path.strip_prefix("/").unwrap_or(path.as_str()); let render = render(path.into(), root.as_ref()); + match render { - Ok(RenderOutput::Rendered(file, content)) | - Ok(RenderOutput::Hidden(file, content)) => { + Ok(RenderOutput::Rendered(_, content)) | + Ok(RenderOutput::Hidden(_, content)) => { Response::builder() .status(200) - .header("Content-Type", "text/html") - .body(content).unwrap() + .body(Body::from(content)).unwrap() + } + Ok(RenderOutput::RawFile(file)) => { + Response::builder() + .status(200) + .body(Body::from(fs::read(file).unwrap_or(vec![]))).unwrap() } - Ok(RenderOutput::RawFile(file)) => { todo!() } Ok(RenderOutput::NoOutput) | Ok(RenderOutput::Dir(_)) => { Response::builder() .status(404) - .body(String::from("Not Found")).unwrap() + .body(Body::from(include_str!("../templates/404.html"))).unwrap() } Err(err) => { Response::builder() .status(500) - .body(format!("{}", err)).unwrap() + .body(Body::from(format!("{}", err))).unwrap() } } } diff --git a/templates/404.html b/templates/404.html new file mode 100644 index 0000000..e1edddd --- /dev/null +++ b/templates/404.html @@ -0,0 +1,9 @@ + + + + Not found + + +Not found + + \ No newline at end of file