Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Render page 404 #71

Merged
merged 8 commits into from
Oct 23, 2024
Merged
5 changes: 5 additions & 0 deletions example/content/404.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
title: Page not found
---

Page not found :/
2 changes: 1 addition & 1 deletion example/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ <h2><a href="{{url_for(path='/')}}" class="contrast">{{ site.name }}</a></h2>
<ul class="header-menu" id="header-menu">
{% for item in menu %}
<li>
{% if current_page == item.1 %}
{% if current_page and current_page == item.1 %}
<button class="secondary">{{item.0 | safe }}</button>
{% else %}
<a href="{{ url_for(path=item.1) }}" class="secondary" {%if item.1 is starting_with("http") %}target="_blank"{% endif %}>{{ item.0 | safe }}</a>
Expand Down
19 changes: 17 additions & 2 deletions src/markdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ use frontmatter_gen::{extract, Frontmatter};
use std::fs;
use std::path::Path;

pub fn process_file(path: &Path, site_data: &mut Data) -> Result<(), String> {
pub fn process_file(
path: &Path,
site_data: &mut Data,
use_filename_as_slug: bool,
) -> Result<(), String> {
let file_content = fs::read_to_string(path).map_err(|e| e.to_string())?;
let (frontmatter, markdown) = parse_front_matter(&file_content)?;
let mut options = ComrakOptions::default();
Expand Down Expand Up @@ -39,7 +43,18 @@ pub fn process_file(path: &Path, site_data: &mut Data) -> Result<(), String> {

let title = get_title(&frontmatter, markdown);
let tags = get_tags(&frontmatter);
let slug = get_slug(&frontmatter, path);
let slug = {
if use_filename_as_slug {
path.file_name()
.expect("Error on getting file name")
.to_str()
.expect("Error on converting file name to string")
.to_string()
.replace(".md", "")
} else {
get_slug(&frontmatter, path)
}
};
let date = get_date(&frontmatter, path);

let extra = frontmatter.get("extra").map(std::borrow::ToOwned::to_owned);
Expand Down
18 changes: 17 additions & 1 deletion src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ fn handle_request(
};

let file_path = output_folder.join(request_path);
let error_path = output_folder.join("404.html");

if file_path.is_file() {
match File::open(&file_path) {
Expand Down Expand Up @@ -70,6 +71,21 @@ fn handle_request(
request_path,
request.http_version()
);
Ok(Response::from_string("404 Not Found").with_status_code(404))
render_not_found(&error_path)
}
}

fn render_not_found(error_path: &PathBuf) -> Result<Response<Cursor<Vec<u8>>>, String> {
match File::open(&error_path) {
Ok(mut file) => {
let mut buffer = Vec::new();
std::io::copy(&mut file, &mut buffer).map_err(|e| e.to_string())?;
let resp = Response::from_data(buffer);
Ok(resp)
}
Err(err) => {
error!("Error on rendering page 404 - {}", err);
Ok(Response::from_string("404 Not Found").with_status_code(404))
}
}
}
50 changes: 48 additions & 2 deletions src/site.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use std::{fs, process, sync::Arc};
use tera::{Context, Tera};
use walkdir::WalkDir;

const NAME_BASED_SLUG_FILES: [&str; 1] = ["404.md"];

#[derive(Serialize)]
pub struct Data<'a> {
pub site: Marmite<'a>,
Expand Down Expand Up @@ -97,6 +99,29 @@ fn render_templates(site_data: &Data, tera: &Tera, output_dir: &Path) -> Result<
)?;
}

// Check and guarantees that page 404 was generated even if 404.md is removed
let file_404_path = output_dir.join("404.html");
if !file_404_path.exists() {
let mut content_context = global_context.clone();
let page_404_content = Content {
html: String::from("Page not found :/"),
title: String::from("Page not found"),
date: None,
slug: String::from(""),
extra: None,
tags: vec![],
};
content_context.insert("title", &page_404_content.title);
content_context.insert("content", &page_404_content);
render_html(
"content.html",
"404.html",
tera,
&content_context,
output_dir,
)?;
}

// Render tagged_contents
let mut unique_tags: Vec<(String, usize)> = Vec::new();
let tags_dir = output_dir.join("tag");
Expand Down Expand Up @@ -347,11 +372,32 @@ fn collect_content(content_dir: &std::path::PathBuf, site_data: &mut Data) {
.into_iter()
.filter_map(Result::ok)
.filter(|e| {
e.path().is_file() && e.path().extension().and_then(|ext| ext.to_str()) == Some("md")
let file_name = e
.path()
.file_name()
.and_then(|ext| ext.to_str())
.expect("Could not get file name");
let file_extension = e.path().extension().and_then(|ext| ext.to_str());
e.path().is_file()
&& !NAME_BASED_SLUG_FILES.contains(&file_name)
&& file_extension == Some("md")
})
.for_each(|entry| {
if let Err(e) = process_file(entry.path(), site_data) {
if let Err(e) = process_file(entry.path(), site_data, false) {
error!("Failed to process file {}: {}", entry.path().display(), e);
}
});

NAME_BASED_SLUG_FILES.into_iter().for_each(|slugged_file| {
let slugged_path = content_dir.join(slugged_file);
if slugged_path.exists() {
if let Err(e) = process_file(slugged_path.as_path(), site_data, true) {
error!(
"Failed to process file {}: {}",
slugged_path.as_path().display(),
e
);
}
}
})
}