Skip to main content

spec_driven_docs/self_depend/
stamp.rs

1//! The per-checkout stamp that holds the shell-entry caller to one attempt
2//! a day.
3//!
4//! The sync runs from the project's shell entry, which fires on every
5//! directory entry. The stamp records the day of the last attempt, success
6//! or failure, so a stale pin costs one network read a day and a broken one
7//! costs nothing more.
8
9use camino::{Utf8Path, Utf8PathBuf};
10use jiff::civil::Date;
11
12use crate::domain::ownership::Sha256;
13use crate::domain::paths::SELF_DEPEND_STAMP_DIR;
14
15/// Where the stamp for one target lives under the state root.
16///
17/// The target's absolute path is digested so two checkouts of one project
18/// keep two stamps.
19#[must_use]
20pub fn path(state_root: &Utf8Path, target: &Utf8Path) -> Utf8PathBuf {
21    let digest = Sha256::of(target.as_str().as_bytes());
22    state_root
23        .join(SELF_DEPEND_STAMP_DIR)
24        .join(format!("{}.stamp", &digest.as_str()[..16]))
25}
26
27/// The day the stamp records, where one is recorded.
28#[must_use]
29pub fn read(path: &Utf8Path) -> Option<Date> {
30    std::fs::read_to_string(path).ok()?.trim().parse().ok()
31}
32
33/// Whether an attempt was already made today.
34#[must_use]
35pub fn attempted(path: &Utf8Path, today: Date) -> bool {
36    read(path) == Some(today)
37}
38
39/// Record today's attempt.
40///
41/// # Errors
42///
43/// The I/O error where the state root cannot be written.
44pub fn mark(path: &Utf8Path, today: Date) -> std::io::Result<()> {
45    if let Some(parent) = path.parent() {
46        std::fs::create_dir_all(parent)?;
47    }
48    crate::adapters::fs::write_atomic(path, format!("{today}\n").as_bytes())
49}
50
51/// Today, in the host's zone.
52#[must_use]
53pub fn today() -> Date {
54    jiff::Zoned::now().date()
55}
56
57#[cfg(test)]
58mod tests {
59    #![allow(clippy::unwrap_used, reason = "a test panics as its failure signal")]
60
61    use super::*;
62
63    /// VERIFIES acquisition:the-shell-entry-caller-is-rate-limited-and-silent
64    #[test]
65    fn a_stamp_holds_one_attempt_per_day_per_checkout() {
66        let dir = tempfile::tempdir().unwrap();
67        let root = Utf8Path::from_path(dir.path()).unwrap();
68        let one = path(root, Utf8Path::new("/work/a"));
69        let two = path(root, Utf8Path::new("/work/b"));
70        assert_ne!(one, two);
71        let day = Date::constant(2026, 9, 15);
72        assert!(!attempted(&one, day));
73        mark(&one, day).unwrap();
74        assert!(attempted(&one, day));
75        assert!(!attempted(&one, Date::constant(2026, 9, 16)));
76        assert!(!attempted(&two, day));
77    }
78}