use std::time::Instant;
use tokio_util::sync::CancellationToken;
use uuid::Uuid;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize)]
#[serde(transparent)]
pub struct RunId(Uuid);
impl RunId {
pub(crate) fn new() -> Self {
Self(Uuid::new_v4())
}
}
impl std::fmt::Display for RunId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:032x}", self.0.as_u128())
}
}
#[derive(Debug)]
pub(crate) struct BackgroundHandle {
pub command: String,
pub started_at: Instant,
pub abort: CancellationToken,
pub child_pid: Option<u32>,
}
impl BackgroundHandle {
pub(crate) fn elapsed(&self) -> std::time::Duration {
self.started_at.elapsed()
}
}
#[derive(Debug, Clone)]
pub struct BackgroundRunSnapshot {
pub run_id: String,
pub command: String,
pub elapsed_ms: u64,
}
#[derive(Debug, Clone)]
pub struct BackgroundCompletion {
pub run_id: RunId,
pub exit_code: i32,
pub output: String,
pub success: bool,
pub elapsed_ms: u64,
pub command: String,
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
#[test]
fn run_id_display_is_32_char_hex() {
let id = RunId::new();
let s = id.to_string();
assert_eq!(s.len(), 32, "RunId should display as 32-char hex");
assert!(
s.chars().all(|c| c.is_ascii_hexdigit()),
"RunId should be lowercase hex, got: {s}"
);
}
#[test]
fn run_id_uniqueness() {
let ids: HashSet<String> = (0..100).map(|_| RunId::new().to_string()).collect();
assert_eq!(ids.len(), 100, "100 RunIds must all be distinct");
}
#[test]
fn run_id_copy_semantics() {
let a = RunId::new();
let b = a; assert_eq!(a, b);
}
}