use std::process::Command; use crate::Session; pub use error::Error; mod error; #[derive(Debug)] pub struct Tmux { socket_name: String, } impl Tmux { const SESSION_FORMAT: &str = r##"Session(name: "#S", state: #{?session_last_attached,Attached(#{session_last_attached}),Created(#{session_created})})"##; pub fn new(socket_name: String) -> Self { Self { socket_name } } pub fn list(&self, name: Option) -> Result, Error> { let filter = name .map(|s| vec!["-f".into(), format!("#{{==:#S,{s}}}")]) .unwrap_or_default(); let stdout = Command::new("tmux") .arg("-L") .arg(&self.socket_name) .arg("list-sessions") .args(filter) .arg("-F") .arg(Self::SESSION_FORMAT) .output()? .stdout; let sessions: Vec = std::str::from_utf8(&stdout)? .lines() .filter_map(|s| match ron::from_str(s) { Ok(session) => Some(session), Err(err) => { tracing::warn!(%err, "Invalid session format"); None } }) .collect(); Ok(sessions) } pub fn host(&self) -> Result { let stdout = Command::new("tmux") .arg("-L") .arg(&self.socket_name) .arg("display") .arg("-p") .arg("#h") .output()? .stdout; let name = std::str::from_utf8(&stdout)?.trim().into(); Ok(Session { state: crate::State::LocalHost, name, }) } } #[cfg(test)] mod tests { use super::*; const SOCKET_NAME: &str = "test"; #[test] fn test_tmux_list() -> Result<(), Error> { let names = Vec::from(["test_1", "test_2", "test_3", "test_4"]); for name in names.iter().cloned() { Command::new("tmux") .arg("-L") .arg(SOCKET_NAME) .arg("new-session") .arg("-ds") .arg(name) .status()?; } let tmux = Tmux::new(SOCKET_NAME.to_owned()); let sessions: Vec<_> = tmux.list(None)?.into_iter().map(|s| s.name).collect(); Command::new("tmux") .arg("-L") .arg(SOCKET_NAME) .arg("kill-server") .status()?; assert_eq!(names, sessions); Ok(()) } #[test] fn test_tmux_host() -> Result<(), Error> { Command::new("tmux") .arg("-L") .arg(SOCKET_NAME) .arg("new-session") .arg("-d") .status()?; let name = hostname::get()?.to_string_lossy().into(); let expected_session = Session { state: crate::State::LocalHost, name, }; let tmux = Tmux::new(SOCKET_NAME.to_owned()); let session = tmux.host()?; Command::new("tmux") .arg("-L") .arg(SOCKET_NAME) .arg("kill-server") .status()?; assert_eq!(expected_session, session); Ok(()) } }