//! Persist the session id between `bsk record start` and `bsk record stop`. use std::fs; use std::time::{SystemTime, UNIX_EPOCH}; use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use crate::daemon::paths; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct RecordSessionState { pub session_id: String, pub started_at: String, } pub fn write(session_id: &str) -> Result<()> { let path = paths::record_session_path()?; paths::ensure_bsk_home()?; if path.exists() { return Err(anyhow::anyhow!( "a recording is already in progress; run `bsk record stop` first" )); } let state = RecordSessionState { session_id: session_id.to_string(), started_at: started_at_unix_ms(), }; let json = serde_json::to_string_pretty(&state).context("serialize record session state")?; fs::write(&path, format!("{json}\n")).with_context(|| format!("write {}", path.display()))?; Ok(()) } pub fn read() -> Result { let path = paths::record_session_path()?; if !path.exists() { return Err(anyhow::anyhow!( "no recording in progress; run `bsk record start` first" )); } let raw = fs::read_to_string(&path).with_context(|| format!("read {}", path.display()))?; serde_json::from_str(&raw).with_context(|| format!("parse {}", path.display())) } pub fn clear() { if let Ok(path) = paths::record_session_path() { let _ = fs::remove_file(path); } } fn started_at_unix_ms() -> String { let ms = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap_or_default() .as_millis(); format!("{ms}") } #[cfg(test)] mod tests { use super::*; fn with_temp_home(f: F) { let _lock = crate::daemon::paths::test_env_lock(); let tmp = tempfile::tempdir().unwrap(); unsafe { std::env::set_var(crate::daemon::paths::BSK_HOME_ENV, tmp.path()); } f(); clear(); unsafe { std::env::remove_var(crate::daemon::paths::BSK_HOME_ENV); } } #[test] fn write_read_round_trips() { with_temp_home(|| { write("abcd").unwrap(); let state = read().unwrap(); assert_eq!(state.session_id, "abcd"); assert!(!state.started_at.is_empty()); }); } #[test] fn write_rejects_duplicate() { with_temp_home(|| { write("abcd").unwrap(); let err = write("efgh").unwrap_err(); assert!(err.to_string().contains("already in progress")); }); } #[test] fn read_errors_when_missing() { with_temp_home(|| { let err = read().unwrap_err(); assert!(err.to_string().contains("no recording in progress")); }); } #[test] fn clear_removes_state_file() { with_temp_home(|| { write("abcd").unwrap(); clear(); assert!(read().is_err()); }); } }