use std::path::Path;
fn shared_stack_count(state_dir: &Path) -> Option<usize> {
let lock = crate::core::state::load_global_lock(state_dir)
.ok()
.flatten()?;
let stacks = super::stack_names(&lock).len();
(stacks > 1).then_some(stacks)
}
#[must_use]
pub fn skip_note(state_dir: &Path, what: &str) -> Option<String> {
let stacks = shared_stack_count(state_dir)?;
Some(format!(
"note: {what} gc skipped: state dir holds {stacks} stacks (PMAT-162)"
))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::state::{new_global_lock, save_global_lock};
use crate::core::types::StackStamp;
fn lock_with(state_dir: &Path, stacks: &[&str]) {
let mut lock = new_global_lock(stacks[0]);
for name in stacks {
lock.stacks.insert(
(*name).to_string(),
StackStamp {
file: Some(format!("../{name}/forjar.yaml")),
last_apply: "2026-01-01T00:00:00Z".to_string(),
generator: "forjar test".to_string(),
machines: vec![(*name).to_string()],
outputs: Vec::new(),
},
);
}
save_global_lock(state_dir, &lock).unwrap();
}
#[test]
fn no_lock_and_one_stack_are_not_shared() {
let dir = tempfile::tempdir().unwrap();
assert_eq!(skip_note(dir.path(), "snapshot"), None, "no global lock");
lock_with(dir.path(), &["alpha"]);
assert_eq!(skip_note(dir.path(), "snapshot"), None, "one stack");
}
#[test]
fn a_shared_dir_is_told_the_count_and_the_ticket() {
let dir = tempfile::tempdir().unwrap();
lock_with(dir.path(), &["alpha", "bravo"]);
assert_eq!(
skip_note(dir.path(), "generation").as_deref(),
Some("note: generation gc skipped: state dir holds 2 stacks (PMAT-162)")
);
assert_eq!(
skip_note(dir.path(), "snapshot").as_deref(),
Some("note: snapshot gc skipped: state dir holds 2 stacks (PMAT-162)")
);
}
}