use std::env;
use std::fs;
use std::io::ErrorKind;
use std::path::PathBuf;
#[cfg(unix)]
use std::os::unix::fs::DirBuilderExt;
use crate::platform;
use crate::session::history::HistoryContext;
fn get_session_dir() -> Result<PathBuf, String> {
let base_dir = env::var("XDG_RUNTIME_DIR")
.or_else(|_| env::var("TMPDIR"))
.unwrap_or_else(|_| "/tmp".to_string());
let uid = platform::get_user_id().map_err(|e| format!("Failed to get user ID: {e}"))?;
let session_dir = PathBuf::from(format!("{base_dir}/whi-{uid}"));
if !session_dir.exists() {
#[cfg(unix)]
{
if let Err(e) = fs::DirBuilder::new().mode(0o700).create(&session_dir)
&& e.kind() != ErrorKind::AlreadyExists
{
return Err(format!("Failed to create session dir: {e}"));
}
}
#[cfg(not(unix))]
{
if let Err(e) = fs::create_dir_all(&session_dir) {
if e.kind() != ErrorKind::AlreadyExists {
return Err(format!("Failed to create session dir: {}", e));
}
}
}
}
Ok(session_dir)
}
pub fn get_session_file(pid: u32) -> Result<PathBuf, String> {
let session_dir = get_session_dir()?;
Ok(session_dir.join(format!("session_{pid}.log")))
}
pub fn write_path_snapshot(pid: u32, path_string: &str) -> Result<(), String> {
HistoryContext::global(pid)?.write_snapshot(path_string)
}
pub fn read_path_snapshots(pid: u32) -> Result<Vec<String>, String> {
HistoryContext::global(pid)?.read_snapshots()
}
pub fn get_initial_path(pid: u32) -> Result<Option<String>, String> {
HistoryContext::global(pid)?.initial_snapshot()
}
pub fn truncate_snapshots(pid: u32, keep_count: usize) -> Result<(), String> {
HistoryContext::global(pid)?.truncate(keep_count)
}
pub fn get_cursor(pid: u32) -> Result<Option<usize>, String> {
HistoryContext::global(pid)?.get_cursor()
}
pub fn set_cursor(pid: u32, position: usize) -> Result<(), String> {
HistoryContext::global(pid)?.set_cursor(position)
}
pub fn clear_cursor(pid: u32) -> Result<(), String> {
HistoryContext::global(pid)?.clear_cursor()
}
pub fn get_current_snapshot(pid: u32) -> Result<Option<String>, String> {
HistoryContext::global(pid)?.current_snapshot()
}
pub fn clear_session(pid: u32) -> Result<(), String> {
HistoryContext::global(pid)?.clear_history()
}
fn get_all_session_files() -> Result<Vec<(PathBuf, std::time::SystemTime)>, String> {
let session_dir = get_session_dir()?;
if !session_dir.exists() {
return Ok(Vec::new());
}
let entries =
fs::read_dir(&session_dir).map_err(|e| format!("Failed to read session directory: {e}"))?;
let mut session_files = Vec::new();
for entry in entries.flatten() {
let path = entry.path();
if let Some(name) = path.file_name().and_then(|n| n.to_str())
&& name.starts_with("session_")
&& path.extension().is_some_and(|ext| ext == "log")
&& let Ok(metadata) = entry.metadata()
&& let Ok(modified) = metadata.modified()
{
session_files.push((path, modified));
}
}
Ok(session_files)
}
pub fn cleanup_old_sessions() -> Result<usize, String> {
let mut session_files = get_all_session_files()?;
if session_files.len() <= 30 {
return Ok(0);
}
session_files.sort_by_key(|entry| entry.1);
let files_to_delete = session_files.len() - 30;
let mut deleted_count = 0;
for (path, _) in session_files.iter().take(files_to_delete) {
if fs::remove_file(path).is_ok() {
deleted_count += 1;
}
}
Ok(deleted_count)
}