tmux_lib/
server.rs

1//! Server management.
2
3use std::collections::HashMap;
4
5use async_std::process::Command;
6
7use crate::{
8    error::{check_empty_process_output, Error},
9    Result,
10};
11
12// ------------------------------
13// Ops
14// ------------------------------
15
16/// Start the Tmux server if needed, creating a session named `"[placeholder]"` in order to keep the server
17/// running.
18///
19/// It is ok-ish to already have an existing session named `"[placeholder]"`.
20pub async fn start(initial_session_name: &str) -> Result<()> {
21    let args = vec!["new-session", "-d", "-s", initial_session_name];
22
23    let output = Command::new("tmux").args(&args).output().await?;
24    check_empty_process_output(&output, "new-session")
25}
26
27/// Remove the session named `"[placeholder]"` used to keep the server alive.
28pub async fn kill_session(name: &str) -> Result<()> {
29    let exact_name = format!("={name}");
30    let args = vec!["kill-session", "-t", &exact_name];
31
32    let output = Command::new("tmux").args(&args).output().await?;
33    check_empty_process_output(&output, "kill-session")
34}
35
36/// Return the value of a Tmux option. For instance, this can be used to get Tmux's default
37/// command.
38pub async fn show_option(option_name: &str, global: bool) -> Result<Option<String>> {
39    let mut args = vec!["show-options", "-w", "-q"];
40    if global {
41        args.push("-g");
42    }
43    args.push(option_name);
44
45    let output = Command::new("tmux").args(&args).output().await?;
46    let buffer = String::from_utf8(output.stdout)?;
47    let buffer = buffer.trim_end();
48
49    if buffer.is_empty() {
50        return Ok(None);
51    }
52    Ok(Some(buffer.to_string()))
53}
54
55/// Return all Tmux options as a `std::haosh::HashMap`.
56pub async fn show_options(global: bool) -> Result<HashMap<String, String>> {
57    let args = if global {
58        vec!["show-options", "-g"]
59    } else {
60        vec!["show-options"]
61    };
62
63    let output = Command::new("tmux").args(&args).output().await?;
64    let buffer = String::from_utf8(output.stdout)?;
65    let pairs: HashMap<String, String> = buffer
66        .trim_end()
67        .split('\n')
68        .map(|s| s.split_at(s.find(' ').unwrap()))
69        .map(|(k, v)| (k, v.trim_start()))
70        .filter(|(_, v)| !v.is_empty() && v != &"''")
71        .map(|(k, v)| (k.to_string(), v.to_string()))
72        .collect();
73
74    Ok(pairs)
75}
76
77/// Return the `"default-command"` used to start a pane, falling back to `"default shell"` if none.
78///
79/// In case of bash, a `-l` flag is added.
80pub async fn default_command() -> Result<String> {
81    let all_options = show_options(true).await?;
82
83    let default_shell = all_options
84        .get("default-shell")
85        .ok_or(Error::TmuxConfig("no default-shell"))
86        .map(|cmd| cmd.to_owned())
87        .map(|cmd| {
88            if cmd.ends_with("bash") {
89                format!("-l {cmd}")
90            } else {
91                cmd
92            }
93        })?;
94
95    all_options
96        .get("default-command")
97        // .map(|cmd| {
98        //     if cmd.trim_end() == "''" {
99        //         &default_shell
100        //     } else {
101        //         cmd
102        //     }
103        // })
104        .or(Some(&default_shell))
105        .ok_or(Error::TmuxConfig("no default-command nor default-shell"))
106        .map(|cmd| cmd.to_owned())
107}