Skip to content

Commit e9f9b8e

Browse files
author
Fleefie
committed
Added dynamically generated index
1 parent 3def440 commit e9f9b8e

File tree

2 files changed

+82
-17
lines changed

2 files changed

+82
-17
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ In order to create your own Goupiblog, you need to:
5252

5353
- Set up your directory structure
5454
- Create your prelude (Or use the one provided!)
55-
- Add an index
55+
- (In the future!) create an index template
5656
- Set up site-wide options
5757
- Create your first post
5858
- Host the post on a simple file server
@@ -61,8 +61,9 @@ We'll get into each of the steps below.
6161

6262
#### The index
6363

64-
The index will be generated for you in the future. For now,
65-
make your own called "index.html" in the root of the sources directory.
64+
For now, the index is generated automatically, but is very basic.
65+
I'll figure out how to make it templated too soon:tm:. Regardless,
66+
it's available under ``target_dir/index.html``
6667

6768
#### The prelude and the options
6869

src/builder.rs

Lines changed: 78 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use std::path::PathBuf;
55
use std::process;
66

77
use std::fs;
8-
use std::fs::copy;
98
use std::io;
109
use std::path::Path;
1110

@@ -16,12 +15,15 @@ use markdown::Constructs;
1615
use markdown::Options;
1716
use markdown::ParseOptions;
1817

18+
use chrono::{DateTime, Local};
19+
1920
#[derive(Debug)]
2021
#[allow(dead_code)]
2122
pub enum PostBuildError {
2223
GeneralIOError(std::io::Error),
2324
TemplateBuildError(std::io::Error),
2425
MissingRequiredKey(String),
26+
PostFilesMissing,
2527
}
2628

2729
#[derive(Debug)]
@@ -34,6 +36,14 @@ pub enum SiteBuildError {
3436
MissingRequiredKey(String),
3537
}
3638

39+
struct PostInfo {
40+
name: String,
41+
title: String,
42+
description: String,
43+
timestamp_display: String,
44+
timestamp: i64,
45+
}
46+
3747
pub fn build_site(source_dir: &PathBuf, output_dir: &PathBuf) -> Result<(), SiteBuildError> {
3848
match fs::create_dir_all(&output_dir) {
3949
Ok(_) => (),
@@ -66,14 +76,6 @@ pub fn build_site(source_dir: &PathBuf, output_dir: &PathBuf) -> Result<(), Site
6676
};
6777
}
6878

69-
let site_index = source_dir.join("index.html");
70-
if site_index.exists() {
71-
match copy(&site_index, &output_dir.join("index.html")) {
72-
Ok(_) => (),
73-
Err(e) => return Err(SiteBuildError::GeneralIOError(e)),
74-
}
75-
}
76-
7779
let prelude_path = source_dir.join("prelude.html");
7880
let prelude = match fs::read_to_string(&prelude_path) {
7981
Ok(content) => content,
@@ -94,6 +96,9 @@ pub fn build_site(source_dir: &PathBuf, output_dir: &PathBuf) -> Result<(), Site
9496
Err(e) => return Err(SiteBuildError::GeneralIOError(e)),
9597
};
9698

99+
// Impure, but I'm lazy.
100+
let mut post_infos: Vec<PostInfo> = Vec::new();
101+
97102
for entry in entries {
98103
let post_dir = match entry {
99104
Ok(p) => p.path(),
@@ -102,7 +107,11 @@ pub fn build_site(source_dir: &PathBuf, output_dir: &PathBuf) -> Result<(), Site
102107

103108
if post_dir.is_dir() {
104109
match process_post(&post_dir, &output_dir, &site_config, &prelude) {
105-
Ok(_) => continue,
110+
Ok(post) => {
111+
if let Some(post_info) = post {
112+
post_infos.push(post_info);
113+
}
114+
}
106115
Err(e) => {
107116
eprintln!("Failed to build post: {e:?}. Continuing...");
108117
continue;
@@ -111,6 +120,27 @@ pub fn build_site(source_dir: &PathBuf, output_dir: &PathBuf) -> Result<(), Site
111120
}
112121
}
113122

123+
// Sort posts by timestamps
124+
post_infos.sort_by(|a, b| b.timestamp.cmp(&a.timestamp));
125+
126+
// Make a simple posts index.
127+
// THIS IS DOGSHIT LMAO
128+
129+
let mut posts_index = String::new();
130+
posts_index.push_str("<html><head><title>Posts</title></head><body>");
131+
posts_index.push_str("<h1>Posts</h1><ul>");
132+
for post in &post_infos {
133+
posts_index.push_str(&format!(
134+
"<li><a href=\"{}/index.html\">{}</a> - {} - {}</li>",
135+
post.name, post.title, post.description, post.timestamp_display
136+
));
137+
}
138+
posts_index.push_str("</ul></body></html>");
139+
match fs::write(output_dir.join("index.html"), posts_index) {
140+
Ok(_) => (),
141+
Err(e) => return Err(SiteBuildError::GeneralIOError(e)),
142+
}
143+
114144
Ok(())
115145
}
116146

@@ -119,12 +149,12 @@ fn process_post(
119149
output_dir: &Path,
120150
site_config: &HashMap<String, toml::Value>,
121151
prelude: &str,
122-
) -> Result<(), PostBuildError> {
152+
) -> Result<Option<PostInfo>, PostBuildError> {
123153
let post_toml_path = post_dir.join("post.toml");
124154
let content_path = post_dir.join("content.md");
125155

126156
if !post_toml_path.exists() || !content_path.exists() {
127-
return Ok(());
157+
return Err(PostBuildError::PostFilesMissing);
128158
}
129159

130160
println!("Processing post: {}", post_dir.display());
@@ -183,6 +213,31 @@ fn process_post(
183213

184214
let post_name = post_dir.file_name().unwrap().to_string_lossy();
185215
let post_output_dir = output_dir.join(&*post_name);
216+
217+
// Check if post is built by checking if the target directory has any
218+
// timestamps older than any timestamp in the source directory.
219+
// If it's older, continue.
220+
// If it's newer, return Ok(None).
221+
let post_build_timestamp = match fs::metadata(&post_output_dir) {
222+
Ok(metadata) => metadata.modified().unwrap(),
223+
Err(_) => std::time::SystemTime::UNIX_EPOCH,
224+
};
225+
let post_source_timestamp = match fs::metadata(&post_dir) {
226+
Ok(metadata) => metadata.modified().unwrap(),
227+
Err(_) => {
228+
return Err(PostBuildError::GeneralIOError(
229+
std::io::Error::new(
230+
std::io::ErrorKind::NotFound,
231+
"Failed to get post directory metadata",
232+
), // NOTE: I should migrate to these errors instead of general IO handling whatever
233+
));
234+
}
235+
};
236+
237+
if post_source_timestamp < post_build_timestamp {
238+
return Ok(None);
239+
}
240+
186241
match fs::create_dir_all(&post_output_dir) {
187242
Ok(_) => (),
188243
Err(e) => return Err(PostBuildError::GeneralIOError(e)),
@@ -208,13 +263,22 @@ fn process_post(
208263
Err(e) => return Err(PostBuildError::GeneralIOError(e)),
209264
}
210265
}
211-
212266
println!(" Built post: {}", post_name);
213267
}
214268
Err(e) => return Err(PostBuildError::TemplateBuildError(e)),
215269
}
216270

217-
Ok(())
271+
let current_local: DateTime<Local> = Local::now();
272+
let current_time = current_local.format("%Y-%m-%d %H:%M:%S").to_string();
273+
274+
Ok(Some(PostInfo {
275+
// Unwrapping is fine here bc we error checked earlier
276+
name: post_name.to_string(),
277+
title: post_config.get("Title").unwrap().to_string(),
278+
description: post_config.get("Description").unwrap().to_string(),
279+
timestamp_display: current_time,
280+
timestamp: current_local.timestamp(),
281+
}))
218282
}
219283

220284
pub fn copy_directory(src: &Path, dst: &Path) -> io::Result<()> {

0 commit comments

Comments
 (0)