Skip to main content

mdql_core/
stamp.rs

1//! Auto-manage created/modified timestamps in frontmatter.
2
3use std::path::Path;
4
5use chrono::NaiveDate;
6
7use crate::txn::atomic_write;
8
9pub const TIMESTAMP_FIELDS: &[&str] = &["created", "modified"];
10
11pub fn stamp_file(
12    path: &Path,
13    now: Option<NaiveDate>,
14) -> crate::errors::Result<StampResult> {
15    let today = now
16        .unwrap_or_else(|| chrono::Local::now().date_naive())
17        .format("%Y-%m-%d")
18        .to_string();
19
20    let text = std::fs::read_to_string(path)?;
21    let lines: Vec<&str> = text.split('\n').collect();
22
23    if lines.is_empty() || lines[0].trim() != "---" {
24        return Ok(StampResult {
25            created_set: false,
26            modified_updated: false,
27        });
28    }
29
30    let mut end_idx = None;
31    for i in 1..lines.len() {
32        if lines[i].trim() == "---" {
33            end_idx = Some(i);
34            break;
35        }
36    }
37
38    let end_idx = match end_idx {
39        Some(i) => i,
40        None => {
41            return Ok(StampResult {
42                created_set: false,
43                modified_updated: false,
44            });
45        }
46    };
47
48    let mut fm_lines: Vec<String> = lines[1..end_idx]
49        .iter()
50        .map(|s| s.to_string())
51        .collect();
52
53    let mut created_idx = None;
54    let mut modified_idx = None;
55    for (i, line) in fm_lines.iter().enumerate() {
56        let stripped = line.trim_start();
57        if stripped.starts_with("created:") || stripped.starts_with("created :") {
58            created_idx = Some(i);
59        } else if stripped.starts_with("modified:") || stripped.starts_with("modified :") {
60            modified_idx = Some(i);
61        }
62    }
63
64    let mut created_set = false;
65    if created_idx.is_none() {
66        fm_lines.push(format!("created: \"{}\"", today));
67        created_set = true;
68    }
69
70    if let Some(idx) = modified_idx {
71        fm_lines[idx] = format!("modified: \"{}\"", today);
72    } else {
73        fm_lines.push(format!("modified: \"{}\"", today));
74    }
75
76    let mut new_lines: Vec<String> = vec!["---".to_string()];
77    new_lines.extend(fm_lines);
78    new_lines.push("---".to_string());
79    for line in &lines[end_idx + 1..] {
80        new_lines.push(line.to_string());
81    }
82
83    atomic_write(path, &new_lines.join("\n"))?;
84
85    Ok(StampResult {
86        created_set,
87        modified_updated: true,
88    })
89}
90
91#[derive(Debug)]
92pub struct StampResult {
93    pub created_set: bool,
94    pub modified_updated: bool,
95}