From b7221cf343579e8ee47142456ffea75f069ef649 Mon Sep 17 00:00:00 2001 From: guus <_@guusw.nl> Date: Sat, 25 Oct 2025 17:52:08 +0800 Subject: [PATCH] better template and add back version --- src/git_tracker.rs | 61 ++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 1 + src/post_manager.rs | 33 +++++++++++++++++------ src/template_engine.rs | 18 +++++++++++++ templates/footer.html | 5 +++- 5 files changed, 109 insertions(+), 9 deletions(-) create mode 100644 src/git_tracker.rs diff --git a/src/git_tracker.rs b/src/git_tracker.rs new file mode 100644 index 0000000..78afd5e --- /dev/null +++ b/src/git_tracker.rs @@ -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 { + 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()); + } + } +} diff --git a/src/main.rs b/src/main.rs index a462e1a..7589a38 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ mod post_manager; mod template_engine; +mod git_tracker; use axum::{ extract::{Path, State}, diff --git a/src/post_manager.rs b/src/post_manager.rs index c6c221e..7dd6b72 100644 --- a/src/post_manager.rs +++ b/src/post_manager.rs @@ -14,15 +14,15 @@ pub struct Post { pub markdown_content: String, pub html_content: String, pub created_at: DateTime, + pub modified_at: DateTime, } struct Entry { #[allow(dead_code)] short_name: String, file_path: String, - created_timestamp: DateTime, - #[allow(dead_code)] - modified_timestamp: DateTime, + created_at: DateTime, + modified_at: DateTime, status: i32, } @@ -47,6 +47,10 @@ impl PostManager { Ok(manager) } + pub fn get_root_dir(&self) -> &PathBuf { + &self.root_dir + } + fn query_posts(&self) -> Result, String> { let output = Command::new("git") .arg("log") @@ -110,13 +114,13 @@ impl PostManager { let entry = result.entry(name.to_string()).or_insert(Entry { short_name: name.to_string(), file_path: file_path.to_string(), - created_timestamp: timestamp, - modified_timestamp: timestamp, + created_at: timestamp, + modified_at: timestamp, status: if status == "D" { -1 } else { 1 }, }); // Always use the oldest timestamp for posts creation dates - entry.created_timestamp = timestamp; + entry.created_at = timestamp; } } } else { @@ -145,9 +149,10 @@ impl PostManager { filename: entry.file_path, markdown_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); } @@ -159,12 +164,24 @@ impl PostManager { self.posts.get(name) } + // Get all posts, sorted by creation date pub fn get_all_posts(&self) -> Vec<&Post> { let mut posts: Vec<&Post> = self.posts.values().collect(); posts.sort_by(|a, b| b.created_at.cmp(&a.created_at)); posts } + // Get the timstamp of when the blog was most recently updated + // derived from latest post update + pub fn get_update_timestamp(&self) -> Result, 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> { let mut posts = self.get_all_posts(); posts.truncate(limit); diff --git a/src/template_engine.rs b/src/template_engine.rs index 0ea1b9a..cddc9e3 100644 --- a/src/template_engine.rs +++ b/src/template_engine.rs @@ -1,4 +1,5 @@ use crate::post_manager::PostManager; +use crate::git_tracker::get_git_version; use std::collections::HashMap; use std::fs; @@ -170,6 +171,23 @@ fn process_tag( 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" => { let limit = attrs.get("limit") .and_then(|s| s.parse::().ok()); diff --git a/templates/footer.html b/templates/footer.html index 23d9902..d5ed8fb 100644 --- a/templates/footer.html +++ b/templates/footer.html @@ -1,5 +1,8 @@