Rust debugging and blog WIP

This commit is contained in:
2025-10-25 00:06:45 +08:00
parent 167ca8c811
commit c11169701f
4 changed files with 56 additions and 182 deletions

View File

@@ -117,7 +117,10 @@ vim.api.nvim_create_user_command("Args", function(a) updateArgs(a.fargs) end,
{ nargs = '*', desc = "Update run/debug arguments" })
if dap_ok then
local lldb_init = {}
local lldb_init = {
"command script import ~/.rustup/toolchains/nightly-2025-02-19-aarch64-apple-darwin/lib/rustlib/etc/lldb_lookup.py",
"command source ~/.rustup/toolchains/nightly-2025-02-19-aarch64-apple-darwin/lib/rustlib/etc/lldb_commands",
}
dap_configs = {
{
name = 'test all',

View File

@@ -1,68 +0,0 @@
use std::fs;
use std::path::Path;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GitVersion {
pub branch: String,
pub commit: String,
}
impl GitVersion {
pub fn key(&self) -> String {
format!("{}/{}", self.branch, self.commit)
}
}
pub fn get_git_version(git_dir: &str) -> Result<GitVersion, String> {
let git_path = Path::new(git_dir);
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,4 +1,3 @@
mod git_tracker;
mod post_manager;
mod template_engine;
@@ -27,18 +26,6 @@ async fn main() {
post_manager: post_manager.clone(),
};
// Spawn background task to watch for git changes
tokio::spawn(async move {
let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(5));
loop {
interval.tick().await;
let mut manager = post_manager.write().await;
if let Err(e) = manager.refresh_if_needed() {
eprintln!("Error refreshing posts: {}", e);
}
}
});
let app = Router::new()
.route("/", get(index_handler))
.route("/:page", get(all_handler))

View File

@@ -1,4 +1,3 @@
use crate::git_tracker::{get_git_version, GitVersion};
use chrono::{DateTime, Utc};
use pulldown_cmark::{html, Options, Parser};
use std::collections::HashMap;
@@ -17,63 +16,43 @@ pub struct Post {
pub created_at: DateTime<Utc>,
}
struct Entry {
#[allow(dead_code)]
short_name: String,
file_path: String,
timestamp: DateTime<Utc>,
status: i32,
}
pub struct PostManager {
git_dir: PathBuf,
posts_dir: PathBuf,
root_dir: PathBuf,
posts_dir_rel: String,
posts: HashMap<String, Post>,
last_git_version: Option<GitVersion>,
}
impl PostManager {
pub fn new(root_dir: &str) -> Result<Self, String> {
let git_dir = PathBuf::from(root_dir);
let posts_dir = git_dir.join("posts");
// Create posts directory if it doesn't exist
if !posts_dir.exists() {
fs::create_dir_all(&posts_dir)
.map_err(|e| format!("Failed to create posts directory: {}", e))?;
}
let root_dir = PathBuf::from(root_dir);
let posts_dir_rel = "posts";
let mut manager = PostManager {
git_dir,
posts_dir,
root_dir,
posts_dir_rel: posts_dir_rel.to_string(),
posts: HashMap::new(),
last_git_version: None,
};
manager.refresh_posts()?;
Ok(manager)
}
pub fn refresh_if_needed(&mut self) -> Result<bool, String> {
let current_version = get_git_version(self.git_dir.to_str().unwrap())?;
if self.last_git_version.as_ref() != Some(&current_version) {
println!(
"Git version changed: {} -> {}",
self.last_git_version
.as_ref()
.map(|v| v.key())
.unwrap_or_else(|| "none".to_string()),
current_version.key()
);
self.refresh_posts()?;
self.last_git_version = Some(current_version);
Ok(true)
} else {
Ok(false)
}
}
fn get_post_timestamps(&self) -> Result<HashMap<String, DateTime<Utc>>, String> {
fn query_posts(&self) -> Result<HashMap<String, Entry>, String> {
let output = Command::new("git")
.arg("whatchanged")
.arg("--pretty=%h - %cd - %s")
.arg("--pretty=%h - %ad - %s")
.arg("--date=unix")
.arg("--")
.arg("posts")
.current_dir(&self.git_dir)
.arg(self.posts_dir_rel.clone())
.current_dir(&self.root_dir)
.output()
.map_err(|e| format!("Failed to execute git whatchanged: {}", e))?;
@@ -85,8 +64,7 @@ impl PostManager {
}
let log_output = String::from_utf8_lossy(&output.stdout);
let mut timestamps: HashMap<String, DateTime<Utc>> = HashMap::new();
let mut result: HashMap<String, Entry> = HashMap::new();
let mut current_timestamp: Option<DateTime<Utc>> = None;
for line in log_output.lines() {
@@ -105,90 +83,64 @@ impl PostManager {
let timestamp_str = after_first_dash[..second_dash_pos].trim();
if let Ok(timestamp) = timestamp_str.parse::<i64>() {
current_timestamp = DateTime::from_timestamp(timestamp, 0);
eprintln!("Timestamp: {:?} ({})", current_timestamp, timestamp);
}
}
}
continue;
}
// Parse file change line: ":000000 100644 0000000 6bdad65 A posts/hello-world-2.md"
else if line.starts_with(':') {
if line.starts_with(':') {
eprintln!("Parsing line: {}", line);
if let Some(timestamp) = current_timestamp {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 6 {
let status = parts[4]; // A (add), D (delete), M (modify)
let file_path = parts[5];
// Only process existing files (not deleted ones)
if status != "D" && file_path.starts_with("posts/") {
if let Some(file_name) = file_path.strip_prefix("posts/") {
if let Some(name) = file_name.strip_suffix(".md") {
// Only update if we don't have a timestamp yet (latest commit wins)
timestamps.entry(name.to_string()).or_insert(timestamp);
}
if let Some(file_name) = file_path.strip_prefix("posts/") {
if let Some(name) = file_name.strip_suffix(".md") {
// Only update if we don't have a timestamp yet (latest commit wins)
result.entry(name.to_string()).or_insert(Entry {
short_name: name.to_string(),
file_path: file_path.to_string(),
timestamp,
status: if status == "D" { -1 } else { 1 },
});
}
}
}
} else {
eprintln!("Invalid git log output, expected prior timestamp: {}", line);
}
}
}
Ok(timestamps)
Ok(result)
}
fn refresh_posts(&mut self) -> Result<(), String> {
self.posts.clear();
// Get timestamps from git history
let git_timestamps = self.get_post_timestamps()?;
let entries = fs::read_dir(&self.posts_dir)
.map_err(|e| format!("Failed to read posts directory: {}", e))?;
for entry in entries {
let entry = entry.map_err(|e| format!("Failed to read directory entry: {}", e))?;
let path = entry.path();
if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("md") {
let filename = path
.file_name()
.and_then(|s| s.to_str())
.ok_or_else(|| "Invalid filename".to_string())?
.to_string();
let name = path
.file_stem()
.and_then(|s| s.to_str())
.ok_or_else(|| "Invalid file stem".to_string())?
.to_string();
let markdown_content = fs::read_to_string(&path)
.map_err(|e| format!("Failed to read post file: {}", e))?;
let html_content = markdown_to_html(&markdown_content);
// Use git timestamp if available, otherwise fall back to file metadata
let created_at = git_timestamps
.get(&name)
.copied()
.or_else(|| {
let metadata = fs::metadata(&path).ok()?;
metadata
.created()
.or_else(|_| metadata.modified())
.ok()
.map(|t| DateTime::<Utc>::from(t))
})
.unwrap_or_else(|| Utc::now());
let post = Post {
name: name.clone(),
filename,
markdown_content,
html_content,
created_at,
};
self.posts.insert(name, post);
let query_result = self.query_posts()?;
for (name, entry) in query_result {
if entry.status != 1 {
continue;
}
let markdown_content = fs::read_to_string(&entry.file_path)
.map_err(|e| format!("Failed to read post file: {}", e))?;
let html_content = markdown_to_html(&markdown_content);
let post = Post {
name: name.clone(),
filename: entry.file_path,
markdown_content,
html_content,
created_at: entry.timestamp,
};
eprintln!("Loaded post: {} ({})", name, entry.timestamp);
self.posts.insert(name, post);
}
println!("Loaded {} posts", self.posts.len());