better template and add back version

This commit is contained in:
2025-10-25 17:52:08 +08:00
parent 190667e5a5
commit b7221cf343
5 changed files with 109 additions and 9 deletions

61
src/git_tracker.rs Normal file
View File

@@ -0,0 +1,61 @@
use std::fs;
use std::path::PathBuf;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GitVersion {
pub branch: String,
pub commit: String,
}
pub fn get_git_version(git_path: &PathBuf) -> Result<GitVersion, String> {
let head_path = git_path.join(".git/HEAD");
// Read HEAD file
let head_content = fs::read_to_string(&head_path)
.map_err(|e| format!("Failed to read .git/HEAD: {}", e))?;
let head_content = head_content.trim();
// Check if HEAD points to a ref
if head_content.starts_with("ref:") {
let ref_path = head_content.strip_prefix("ref:").unwrap().trim();
let full_ref_path = git_path.join(".git").join(ref_path);
// Read the ref file to get the commit hash
let commit_hash = fs::read_to_string(&full_ref_path)
.map_err(|e| format!("Failed to read ref file: {}", e))?
.trim()
.to_string();
// Extract branch name from ref path
let branch = ref_path
.strip_prefix("refs/heads/")
.unwrap_or(ref_path)
.to_string();
Ok(GitVersion {
branch,
commit: commit_hash,
})
} else {
// Detached HEAD state - just use the commit hash
Ok(GitVersion {
branch: "detached".to_string(),
commit: head_content.to_string(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_git_version() {
// This test will only work if run in a git repository
if let Ok(version) = get_git_version(".") {
assert!(!version.commit.is_empty());
assert!(!version.branch.is_empty());
}
}
}

View File

@@ -1,5 +1,6 @@
mod post_manager; mod post_manager;
mod template_engine; mod template_engine;
mod git_tracker;
use axum::{ use axum::{
extract::{Path, State}, extract::{Path, State},

View File

@@ -14,15 +14,15 @@ pub struct Post {
pub markdown_content: String, pub markdown_content: String,
pub html_content: String, pub html_content: String,
pub created_at: DateTime<Utc>, pub created_at: DateTime<Utc>,
pub modified_at: DateTime<Utc>,
} }
struct Entry { struct Entry {
#[allow(dead_code)] #[allow(dead_code)]
short_name: String, short_name: String,
file_path: String, file_path: String,
created_timestamp: DateTime<Utc>, created_at: DateTime<Utc>,
#[allow(dead_code)] modified_at: DateTime<Utc>,
modified_timestamp: DateTime<Utc>,
status: i32, status: i32,
} }
@@ -47,6 +47,10 @@ impl PostManager {
Ok(manager) Ok(manager)
} }
pub fn get_root_dir(&self) -> &PathBuf {
&self.root_dir
}
fn query_posts(&self) -> Result<HashMap<String, Entry>, String> { fn query_posts(&self) -> Result<HashMap<String, Entry>, String> {
let output = Command::new("git") let output = Command::new("git")
.arg("log") .arg("log")
@@ -110,13 +114,13 @@ impl PostManager {
let entry = result.entry(name.to_string()).or_insert(Entry { let entry = result.entry(name.to_string()).or_insert(Entry {
short_name: name.to_string(), short_name: name.to_string(),
file_path: file_path.to_string(), file_path: file_path.to_string(),
created_timestamp: timestamp, created_at: timestamp,
modified_timestamp: timestamp, modified_at: timestamp,
status: if status == "D" { -1 } else { 1 }, status: if status == "D" { -1 } else { 1 },
}); });
// Always use the oldest timestamp for posts creation dates // Always use the oldest timestamp for posts creation dates
entry.created_timestamp = timestamp; entry.created_at = timestamp;
} }
} }
} else { } else {
@@ -145,9 +149,10 @@ impl PostManager {
filename: entry.file_path, filename: entry.file_path,
markdown_content, markdown_content,
html_content, html_content,
created_at: entry.created_timestamp, created_at: entry.created_at,
modified_at: entry.modified_at,
}; };
eprintln!("Loaded post: {} ({})", name, entry.created_timestamp); eprintln!("Loaded post: {} ({})", name, entry.created_at);
self.posts.insert(name, post); self.posts.insert(name, post);
} }
@@ -159,12 +164,24 @@ impl PostManager {
self.posts.get(name) self.posts.get(name)
} }
// Get all posts, sorted by creation date
pub fn get_all_posts(&self) -> Vec<&Post> { pub fn get_all_posts(&self) -> Vec<&Post> {
let mut posts: Vec<&Post> = self.posts.values().collect(); let mut posts: Vec<&Post> = self.posts.values().collect();
posts.sort_by(|a, b| b.created_at.cmp(&a.created_at)); posts.sort_by(|a, b| b.created_at.cmp(&a.created_at));
posts posts
} }
// Get the timstamp of when the blog was most recently updated
// derived from latest post update
pub fn get_update_timestamp(&self) -> Result<DateTime<Utc>, String> {
let mut posts: Vec<&Post> = self.posts.values().collect();
posts.sort_by(|a, b| b.modified_at.cmp(&a.modified_at));
Ok(posts
.first()
.ok_or("No posts found".to_string())?
.created_at)
}
pub fn get_posts_limited(&self, limit: usize) -> Vec<&Post> { pub fn get_posts_limited(&self, limit: usize) -> Vec<&Post> {
let mut posts = self.get_all_posts(); let mut posts = self.get_all_posts();
posts.truncate(limit); posts.truncate(limit);

View File

@@ -1,4 +1,5 @@
use crate::post_manager::PostManager; use crate::post_manager::PostManager;
use crate::git_tracker::get_git_version;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs; use std::fs;
@@ -170,6 +171,23 @@ fn process_tag(
Ok(post.html_content.clone()) Ok(post.html_content.clone())
} }
"updated" => {
// Insert the last updated time of the current post (or most recent post)
let post_ts = if let Some(p) = current_post {
post_manager.get_post(p).ok_or_else(|| format!("Post '{}' not found", p))?.modified_at
} else {
post_manager.get_update_timestamp()?
};
Ok(post_ts.format("%Y-%m-%d").to_string())
}
"version" => {
let git_dir = post_manager.get_root_dir().clone();
let git_version = get_git_version(&git_dir)?;
Ok(format!("{}/{}", git_version.branch, git_version.commit))
}
"post-list" => { "post-list" => {
let limit = attrs.get("limit") let limit = attrs.get("limit")
.and_then(|s| s.parse::<usize>().ok()); .and_then(|s| s.parse::<usize>().ok());

View File

@@ -1,5 +1,8 @@
<footer style="margin-top: 50px; padding-top: 20px; border-top: 1px solid #ddd; color: #666; text-align: center;"> <footer style="margin-top: 50px; padding-top: 20px; border-top: 1px solid #ddd; color: #666; text-align: center;">
<p>Guus Waals - updated on $<updated/> ($<version/>)</p> <p>
Guus Waals - updated on $<updated/> </br>
$<version/>
</p>
</footer> </footer>
</body> </body>
</html> </html>