use std::process::Command; use crate::Session; use clap::Args; pub use error::Error; mod error; #[derive(Debug, Clone, Args)] #[group(skip)] pub struct Tmux { /// tmux socket-name, equivelent to `tmux -L ` #[arg(short = 'L', long = "tmux_socket", default_value = "ssh")] pub socket: String, #[arg(short = 'E', long)] pub exclude_attached: bool, } impl Tmux { const SESSION_FORMAT: &str = r##"Session(name: "#S", state: #{?session_last_attached,Attached(#{session_last_attached}),Created(#{session_created})})"##; pub fn list(&self) -> Result, Error> { let stdout = Command::new("tmux") .arg("-L") .arg(&self.socket) .arg("list-sessions") .arg("-F") .arg(Self::SESSION_FORMAT) .output()? .stdout; std::str::from_utf8(&stdout)? .lines() .map(ron::from_str) .collect::>() .map_err(Into::into) } pub fn attached(&self) -> Result { let stdout = Command::new("tmux") .arg("-L") .arg(&self.socket) .arg("display") .arg("-p") .arg(Self::SESSION_FORMAT) .output()? .stdout; std::str::from_utf8(&stdout)? .lines() .map(ron::from_str) .last() .transpose()? .ok_or(Error::NotFound) } } #[cfg(test)] mod tests { use super::*; const SOCKET: &str = "test"; #[test] #[ignore] 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) .arg("new-session") .arg("-ds") .arg(name) .status()?; } let tmux = Tmux { socket: SOCKET.to_owned(), exclude_attached: false, }; let sessions: Vec<_> = tmux.list()?.into_iter().map(|s| s.name).collect(); Command::new("tmux") .arg("-L") .arg(SOCKET) .arg("kill-server") .status()?; assert_eq!(names, sessions); Ok(()) } }