use std::path::{Path, PathBuf};
use std::time::{Duration, Instant};
use anyhow::{Context, Result};
pub(crate) const SLOT_WAIT_MS: u64 = 5000;
pub(crate) const SLOT_POLL_MS: u64 = 50;
#[derive(Debug, Clone)]
pub(crate) struct SlotChain {
pub(crate) dir: PathBuf,
pub(crate) slot: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum WaitOutcome {
First,
Ready,
TimedOut,
}
pub(crate) fn seq_dir(ppid: u32, event: &str, lane: &str) -> PathBuf {
std::env::temp_dir().join(format!(
"csift-deliver-{ppid}-{}-{}",
sanitize_component(event),
sanitize_component(lane)
))
}
fn sanitize_component(s: &str) -> String {
let folded: String = s
.chars()
.map(|c| {
if c.is_ascii_alphanumeric() || c == '_' {
c
} else {
'-'
}
})
.collect();
if folded.is_empty() {
"x".to_string()
} else {
folded
}
}
pub(crate) fn open_slot(ppid: u32, event: &str, lane: &str, slot: u32) -> Result<SlotChain> {
let dir = seq_dir(ppid, event, lane);
if slot <= 1 {
let _ = std::fs::remove_dir_all(&dir);
}
std::fs::create_dir_all(&dir)
.with_context(|| format!("creating slot chain directory {}", dir.display()))?;
Ok(SlotChain { dir, slot })
}
fn marker_path(dir: &Path, slot: u32) -> PathBuf {
dir.join(format!("s{slot}.done"))
}
pub(crate) fn wait_prev(chain: &SlotChain) -> WaitOutcome {
wait_prev_deadline(chain, SLOT_WAIT_MS)
}
pub(crate) fn wait_prev_deadline(chain: &SlotChain, deadline_ms: u64) -> WaitOutcome {
if chain.slot <= 1 {
return WaitOutcome::First;
}
let target = marker_path(&chain.dir, chain.slot - 1);
let started = Instant::now();
let deadline = Duration::from_millis(deadline_ms);
loop {
if target.exists() {
return WaitOutcome::Ready;
}
if started.elapsed() >= deadline {
return WaitOutcome::TimedOut;
}
std::thread::sleep(Duration::from_millis(SLOT_POLL_MS));
}
}
pub(crate) fn mark_done(chain: &SlotChain) -> Result<()> {
let path = marker_path(&chain.dir, chain.slot);
std::fs::write(&path, b"").with_context(|| format!("writing slot marker {}", path.display()))
}
pub(crate) fn cleanup_if_last(chain: &SlotChain, max_slot: u32) {
if chain.slot >= max_slot {
let _ = std::fs::remove_dir_all(&chain.dir);
}
}
pub(crate) fn disorder_warning(slot: u32) -> String {
format!(
"[csift-channel warning: slot {slot} emitted before slot {}; order may be disturbed]",
slot.saturating_sub(1)
)
}