Rust debugging and blog WIP
This commit is contained in:
@@ -117,7 +117,10 @@ vim.api.nvim_create_user_command("Args", function(a) updateArgs(a.fargs) end,
|
|||||||
{ nargs = '*', desc = "Update run/debug arguments" })
|
{ nargs = '*', desc = "Update run/debug arguments" })
|
||||||
|
|
||||||
if dap_ok then
|
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 = {
|
dap_configs = {
|
||||||
{
|
{
|
||||||
name = 'test all',
|
name = 'test all',
|
||||||
|
|||||||
@@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
13
src/main.rs
13
src/main.rs
@@ -1,4 +1,3 @@
|
|||||||
mod git_tracker;
|
|
||||||
mod post_manager;
|
mod post_manager;
|
||||||
mod template_engine;
|
mod template_engine;
|
||||||
|
|
||||||
@@ -27,18 +26,6 @@ async fn main() {
|
|||||||
post_manager: post_manager.clone(),
|
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()
|
let app = Router::new()
|
||||||
.route("/", get(index_handler))
|
.route("/", get(index_handler))
|
||||||
.route("/:page", get(all_handler))
|
.route("/:page", get(all_handler))
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use crate::git_tracker::{get_git_version, GitVersion};
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use pulldown_cmark::{html, Options, Parser};
|
use pulldown_cmark::{html, Options, Parser};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -17,63 +16,43 @@ pub struct Post {
|
|||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Entry {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
short_name: String,
|
||||||
|
file_path: String,
|
||||||
|
timestamp: DateTime<Utc>,
|
||||||
|
status: i32,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct PostManager {
|
pub struct PostManager {
|
||||||
git_dir: PathBuf,
|
root_dir: PathBuf,
|
||||||
posts_dir: PathBuf,
|
posts_dir_rel: String,
|
||||||
posts: HashMap<String, Post>,
|
posts: HashMap<String, Post>,
|
||||||
last_git_version: Option<GitVersion>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PostManager {
|
impl PostManager {
|
||||||
pub fn new(root_dir: &str) -> Result<Self, String> {
|
pub fn new(root_dir: &str) -> Result<Self, String> {
|
||||||
let git_dir = PathBuf::from(root_dir);
|
let root_dir = PathBuf::from(root_dir);
|
||||||
let posts_dir = git_dir.join("posts");
|
let posts_dir_rel = "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 mut manager = PostManager {
|
let mut manager = PostManager {
|
||||||
git_dir,
|
root_dir,
|
||||||
posts_dir,
|
posts_dir_rel: posts_dir_rel.to_string(),
|
||||||
posts: HashMap::new(),
|
posts: HashMap::new(),
|
||||||
last_git_version: None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
manager.refresh_posts()?;
|
manager.refresh_posts()?;
|
||||||
Ok(manager)
|
Ok(manager)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn refresh_if_needed(&mut self) -> Result<bool, String> {
|
fn query_posts(&self) -> Result<HashMap<String, Entry>, String> {
|
||||||
let current_version = get_git_version(self.git_dir.to_str().unwrap())?;
|
|
||||||
|
|
||||||
if self.last_git_version.as_ref() != Some(¤t_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> {
|
|
||||||
let output = Command::new("git")
|
let output = Command::new("git")
|
||||||
.arg("whatchanged")
|
.arg("whatchanged")
|
||||||
.arg("--pretty=%h - %cd - %s")
|
.arg("--pretty=%h - %ad - %s")
|
||||||
.arg("--date=unix")
|
.arg("--date=unix")
|
||||||
.arg("--")
|
.arg("--")
|
||||||
.arg("posts")
|
.arg(self.posts_dir_rel.clone())
|
||||||
.current_dir(&self.git_dir)
|
.current_dir(&self.root_dir)
|
||||||
.output()
|
.output()
|
||||||
.map_err(|e| format!("Failed to execute git whatchanged: {}", e))?;
|
.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 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;
|
let mut current_timestamp: Option<DateTime<Utc>> = None;
|
||||||
|
|
||||||
for line in log_output.lines() {
|
for line in log_output.lines() {
|
||||||
@@ -105,91 +83,65 @@ impl PostManager {
|
|||||||
let timestamp_str = after_first_dash[..second_dash_pos].trim();
|
let timestamp_str = after_first_dash[..second_dash_pos].trim();
|
||||||
if let Ok(timestamp) = timestamp_str.parse::<i64>() {
|
if let Ok(timestamp) = timestamp_str.parse::<i64>() {
|
||||||
current_timestamp = DateTime::from_timestamp(timestamp, 0);
|
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"
|
// 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 {
|
if let Some(timestamp) = current_timestamp {
|
||||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||||
if parts.len() >= 6 {
|
if parts.len() >= 6 {
|
||||||
let status = parts[4]; // A (add), D (delete), M (modify)
|
let status = parts[4]; // A (add), D (delete), M (modify)
|
||||||
let file_path = parts[5];
|
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(file_name) = file_path.strip_prefix("posts/") {
|
||||||
if let Some(name) = file_name.strip_suffix(".md") {
|
if let Some(name) = file_name.strip_suffix(".md") {
|
||||||
// Only update if we don't have a timestamp yet (latest commit wins)
|
// Only update if we don't have a timestamp yet (latest commit wins)
|
||||||
timestamps.entry(name.to_string()).or_insert(timestamp);
|
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> {
|
fn refresh_posts(&mut self) -> Result<(), String> {
|
||||||
self.posts.clear();
|
self.posts.clear();
|
||||||
|
|
||||||
// Get timestamps from git history
|
// Get timestamps from git history
|
||||||
let git_timestamps = self.get_post_timestamps()?;
|
let query_result = self.query_posts()?;
|
||||||
|
for (name, entry) in query_result {
|
||||||
let entries = fs::read_dir(&self.posts_dir)
|
if entry.status != 1 {
|
||||||
.map_err(|e| format!("Failed to read posts directory: {}", e))?;
|
continue;
|
||||||
|
}
|
||||||
for entry in entries {
|
let markdown_content = fs::read_to_string(&entry.file_path)
|
||||||
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))?;
|
.map_err(|e| format!("Failed to read post file: {}", e))?;
|
||||||
|
|
||||||
let html_content = markdown_to_html(&markdown_content);
|
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 {
|
let post = Post {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
filename,
|
filename: entry.file_path,
|
||||||
markdown_content,
|
markdown_content,
|
||||||
html_content,
|
html_content,
|
||||||
created_at,
|
created_at: entry.timestamp,
|
||||||
};
|
};
|
||||||
|
eprintln!("Loaded post: {} ({})", name, entry.timestamp);
|
||||||
self.posts.insert(name, post);
|
self.posts.insert(name, post);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
println!("Loaded {} posts", self.posts.len());
|
println!("Loaded {} posts", self.posts.len());
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
Reference in New Issue
Block a user