use std::sync::{Arc, Mutex};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SkipEvent {
pub stage: String,
pub label: String,
pub reason: String,
}
#[derive(Debug, Clone, Default)]
pub struct SkipMemento {
inner: Arc<Mutex<Vec<SkipEvent>>>,
}
impl SkipMemento {
pub fn new() -> Self {
Self::default()
}
pub fn remember(&self, stage: &str, label: &str, reason: &str) {
let event = SkipEvent {
stage: stage.to_string(),
label: label.to_string(),
reason: reason.to_string(),
};
let mut guard = match self.inner.lock() {
Ok(g) => g,
Err(poisoned) => poisoned.into_inner(),
};
if !guard.iter().any(|e| e == &event) {
guard.push(event);
}
}
pub fn len(&self) -> usize {
self.inner.lock().map(|g| g.len()).unwrap_or(0)
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn snapshot(&self) -> Vec<SkipEvent> {
self.inner.lock().map(|g| g.clone()).unwrap_or_default()
}
pub fn drain(&self) -> Vec<SkipEvent> {
self.inner
.lock()
.map(|mut g| std::mem::take(&mut *g))
.unwrap_or_default()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn records_and_drains() {
let m = SkipMemento::new();
assert!(m.is_empty());
m.remember("sign", "cosign", "artifacts: none");
m.remember("sign", "gpg", "if: false");
assert_eq!(m.len(), 2);
let drained = m.drain();
assert_eq!(drained.len(), 2);
assert_eq!(drained[0].stage, "sign");
assert_eq!(drained[0].label, "cosign");
assert_eq!(drained[0].reason, "artifacts: none");
assert!(m.is_empty());
}
#[test]
fn deduplicates_identical_events() {
let m = SkipMemento::new();
for _ in 0..10 {
m.remember("sign", "cosign", "ids filter matched no artifacts");
}
assert_eq!(m.len(), 1);
}
#[test]
fn keeps_distinct_reasons_per_label() {
let m = SkipMemento::new();
m.remember("sign", "cosign", "artifacts: none");
m.remember("sign", "cosign", "if: false");
assert_eq!(m.len(), 2);
}
#[test]
fn snapshot_does_not_clear() {
let m = SkipMemento::new();
m.remember("publisher", "my-tool", "empty cmd");
let snap = m.snapshot();
assert_eq!(snap.len(), 1);
assert_eq!(m.len(), 1);
}
#[test]
fn clone_shares_state() {
let m = SkipMemento::new();
let m2 = m.clone();
m2.remember("docker-sign", "cosign-docker", "artifacts: none");
assert_eq!(m.len(), 1);
assert_eq!(m.snapshot(), m2.snapshot());
}
}