use std::fs;
use std::path::{Path, PathBuf};
use crate::connection::get_socket_dir;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SessionFile {
Socket,
Port,
Pid,
Version,
Config,
Stream,
Engine,
Provider,
Extensions,
Target,
Settings,
Log,
}
impl SessionFile {
pub const ALL: &'static [SessionFile] = &[
SessionFile::Socket,
SessionFile::Port,
SessionFile::Pid,
SessionFile::Version,
SessionFile::Config,
SessionFile::Stream,
SessionFile::Engine,
SessionFile::Provider,
SessionFile::Extensions,
SessionFile::Target,
SessionFile::Settings,
SessionFile::Log,
];
pub fn extension(self) -> &'static str {
match self {
SessionFile::Socket => "sock",
SessionFile::Port => "port",
SessionFile::Pid => "pid",
SessionFile::Version => "version",
SessionFile::Config => "config",
SessionFile::Stream => "stream",
SessionFile::Engine => "engine",
SessionFile::Provider => "provider",
SessionFile::Extensions => "extensions",
SessionFile::Target => "target",
SessionFile::Settings => "settings",
SessionFile::Log => "log",
}
}
pub fn is_daemon_file(self) -> bool {
match self {
SessionFile::Socket
| SessionFile::Port
| SessionFile::Pid
| SessionFile::Version
| SessionFile::Config
| SessionFile::Stream
| SessionFile::Engine
| SessionFile::Provider
| SessionFile::Extensions => true,
SessionFile::Target | SessionFile::Settings | SessionFile::Log => false,
}
}
pub fn path(self, session: &str) -> PathBuf {
self.path_in(&get_socket_dir(), session)
}
pub fn path_in(self, dir: &Path, session: &str) -> PathBuf {
dir.join(format!("{}.{}", session, self.extension()))
}
}
pub fn remove_daemon_files(dir: &Path, session: &str) {
for file in SessionFile::ALL.iter().filter(|f| f.is_daemon_file()) {
let _ = fs::remove_file(file.path_in(dir, session));
}
}
pub fn owns_session(dir: &Path, session: &str, pid: u32) -> bool {
fs::read_to_string(SessionFile::Pid.path_in(dir, session))
.ok()
.and_then(|s| s.trim().parse::<u32>().ok())
== Some(pid)
}
#[cfg(test)]
mod tests {
use super::*;
fn write_all_files(dir: &Path, session: &str) {
for file in SessionFile::ALL {
fs::write(file.path_in(dir, session), "x").unwrap();
}
}
#[test]
fn test_all_has_unique_extensions() {
let mut extensions: Vec<_> = SessionFile::ALL.iter().map(|f| f.extension()).collect();
extensions.sort_unstable();
extensions.dedup();
assert_eq!(extensions.len(), SessionFile::ALL.len());
}
#[test]
fn test_remove_daemon_files_keeps_persistent_files() {
let dir = tempfile::tempdir().unwrap();
write_all_files(dir.path(), "s");
remove_daemon_files(dir.path(), "s");
let mut left: Vec<String> = fs::read_dir(dir.path())
.unwrap()
.map(|e| e.unwrap().file_name().to_string_lossy().into_owned())
.collect();
left.sort();
assert_eq!(left, ["s.log", "s.settings", "s.target"]);
assert_eq!(
fs::read_to_string(SessionFile::Target.path_in(dir.path(), "s")).unwrap(),
"x"
);
}
#[test]
fn test_settings_file_is_persistent_and_named_after_session() {
assert!(!SessionFile::Settings.is_daemon_file());
let dir = Path::new("/d");
assert_eq!(
SessionFile::Settings.path_in(dir, "work"),
Path::new("/d/work.settings")
);
}
#[test]
fn test_remove_daemon_files_leaves_other_sessions() {
let dir = tempfile::tempdir().unwrap();
write_all_files(dir.path(), "other");
remove_daemon_files(dir.path(), "s");
for file in SessionFile::ALL {
assert!(file.path_in(dir.path(), "other").exists());
}
}
#[test]
fn test_owns_session_when_pid_matches() {
let dir = tempfile::tempdir().unwrap();
fs::write(SessionFile::Pid.path_in(dir.path(), "s"), "4242").unwrap();
assert!(owns_session(dir.path(), "s", 4242));
}
#[test]
fn test_owns_session_false_for_newer_daemon() {
let dir = tempfile::tempdir().unwrap();
fs::write(SessionFile::Pid.path_in(dir.path(), "s"), "5151\n").unwrap();
assert!(!owns_session(dir.path(), "s", 4242));
}
#[test]
fn test_owns_session_false_without_pid_file() {
let dir = tempfile::tempdir().unwrap();
assert!(!owns_session(dir.path(), "s", 4242));
}
}