pub mod screen;
pub mod tmux;
pub mod zellij;
use std::collections::HashMap;
pub trait Focuser {
fn focus(&self, tty: &str, client_pid: Option<i32>) -> bool;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Multiplexer {
Tmux,
Zellij,
Screen,
}
#[derive(Debug, Clone)]
pub enum MuxFocusResult {
Switched {
session: String,
window: String,
pane: String,
},
NoClient {
session: String,
},
NotFound,
Error(String),
}
#[must_use]
pub fn detect<S: std::hash::BuildHasher>(
pid: i32,
parent_map: &HashMap<i32, i32, S>,
) -> Option<Multiplexer> {
let mut current = pid;
for _ in 0..50 {
if let Some(comm) = prock::get_process_comm(current) {
if comm.contains("tmux") {
return Some(Multiplexer::Tmux);
}
if comm.contains("zellij") {
return Some(Multiplexer::Zellij);
}
if comm == "screen" || comm == "SCREEN" {
return Some(Multiplexer::Screen);
}
}
current = *parent_map.get(¤t)?;
if current <= 1 {
break;
}
}
None
}
pub fn focus<S>(
mux: Multiplexer,
pid: i32,
parent_map: &HashMap<i32, i32, S>,
focuser: &dyn Focuser,
) -> MuxFocusResult
where
S: std::hash::BuildHasher,
{
match mux {
Multiplexer::Tmux => tmux::focus_pane(pid, parent_map, focuser),
Multiplexer::Zellij => zellij::focus_pane(pid, parent_map, focuser),
Multiplexer::Screen => screen::focus_pane(pid, parent_map, focuser),
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::Cell;
struct MockFocuser {
called: Cell<bool>,
should_succeed: bool,
}
impl MockFocuser {
fn new(should_succeed: bool) -> Self {
Self {
called: Cell::new(false),
should_succeed,
}
}
fn was_called(&self) -> bool {
self.called.get()
}
}
impl Focuser for MockFocuser {
fn focus(&self, _tty: &str, _client_pid: Option<i32>) -> bool {
self.called.set(true);
self.should_succeed
}
}
#[test]
fn test_detect_no_mux() {
let parent_map = prock::build_parent_map();
let _ = detect(std::process::id() as i32, &parent_map);
}
#[test]
fn test_focus_with_mock_focuser() {
let parent_map = prock::build_parent_map();
let focuser = MockFocuser::new(true);
let result = focus(Multiplexer::Tmux, 999_999_999, &parent_map, &focuser);
assert!(matches!(result, MuxFocusResult::NotFound));
assert!(!focuser.was_called());
}
}