Skip to main content

muxr/cmds/
start.rs

1use std::path::Path;
2use std::path::PathBuf;
3
4use muxr_core::SessionName;
5use rootcause::prelude::ResultExt;
6use rootcause::report;
7
8const SERVER_EXECUTABLE: &str = "muxr-server";
9
10pub fn run(session: &SessionName, external_layout: Option<PathBuf>) -> rootcause::Result<()> {
11    let current_exe = std::env::current_exe().context("failed to resolve muxr executable")?;
12    let server_executable = server_executable_next_to(&current_exe)?;
13    let external_layout = match external_layout {
14        Some(path) if path.is_relative() => Some(
15            std::env::current_dir()
16                .context("failed to resolve muxr cwd")?
17                .join(path),
18        ),
19        Some(path) => Some(path),
20        None => None,
21    };
22    muxr_client::start(session, &server_executable, external_layout.as_deref())?;
23    Ok(())
24}
25
26pub(super) fn server_executable_next_to(current_exe: &Path) -> rootcause::Result<PathBuf> {
27    let Some(parent) = current_exe.parent().filter(|parent| !parent.as_os_str().is_empty()) else {
28        return Err(
29            report!("muxr executable has no parent dir").attach(format!("executable={}", current_exe.display()))
30        );
31    };
32    // Keep the attached client and long-lived server as separate processes: `muxr` can link picker/UI-only CLI deps,
33    // while `muxr-server` keeps session state, PTYs, and scrollback memory attributable to the server runtime alone.
34    Ok(parent.join(SERVER_EXECUTABLE))
35}
36
37#[cfg(test)]
38mod tests {
39    use test_that::prelude::*;
40
41    use super::*;
42
43    #[test]
44    fn test_server_executable_next_to_returns_sibling_without_checking_existence() -> rootcause::Result<()> {
45        let tempdir = tempfile::tempdir()?;
46        let muxr = tempdir.path().join("muxr");
47        let runner = tempdir.path().join(SERVER_EXECUTABLE);
48
49        assert_that!(server_executable_next_to(&muxr)?, eq(runner));
50        Ok(())
51    }
52}