Skip to main content

lean_ctx/ipc/
mod.rs

1pub mod process;
2
3#[cfg(unix)]
4mod unix;
5#[cfg(windows)]
6mod windows;
7#[cfg(windows)]
8pub use windows::NamedPipeListener;
9
10#[cfg(unix)]
11use std::path::PathBuf;
12
13use anyhow::Result;
14
15/// Platform-independent daemon address.
16#[derive(Debug, Clone)]
17pub enum DaemonAddr {
18    #[cfg(unix)]
19    Unix(PathBuf),
20    #[cfg(windows)]
21    NamedPipe(String),
22}
23
24impl DaemonAddr {
25    pub fn default_for_current_os() -> Self {
26        #[cfg(unix)]
27        {
28            Self::Unix(unix::default_socket_path())
29        }
30        #[cfg(windows)]
31        {
32            Self::NamedPipe(windows::default_pipe_name())
33        }
34    }
35
36    pub fn display(&self) -> String {
37        match self {
38            #[cfg(unix)]
39            Self::Unix(p) => p.display().to_string(),
40            #[cfg(windows)]
41            Self::NamedPipe(n) => n.clone(),
42        }
43    }
44
45    /// Check whether anything is currently listening on this address.
46    pub fn is_listening(&self) -> bool {
47        match self {
48            #[cfg(unix)]
49            Self::Unix(p) => p.exists(),
50            #[cfg(windows)]
51            Self::NamedPipe(name) => windows::pipe_exists(name),
52        }
53    }
54}
55
56/// Remove any stale IPC endpoint (socket file / pipe marker).
57pub fn cleanup(addr: &DaemonAddr) {
58    match addr {
59        #[cfg(unix)]
60        DaemonAddr::Unix(p) => {
61            if p.exists() {
62                let _ = std::fs::remove_file(p);
63            }
64        }
65        #[cfg(windows)]
66        DaemonAddr::NamedPipe(_) => {
67            // Named pipes are kernel objects — no cleanup needed.
68        }
69    }
70}
71
72/// Bind a listener on the given address and return a platform-specific listener.
73#[cfg(unix)]
74pub fn bind_listener(addr: &DaemonAddr) -> Result<tokio::net::UnixListener> {
75    match addr {
76        DaemonAddr::Unix(path) => unix::bind_listener(path),
77    }
78}
79
80/// Connect to the daemon at the given address.
81#[cfg(unix)]
82pub async fn connect(addr: &DaemonAddr) -> Result<tokio::net::UnixStream> {
83    match addr {
84        DaemonAddr::Unix(path) => unix::connect(path).await,
85    }
86}
87
88/// Bind a listener on the given Windows named pipe address.
89#[cfg(windows)]
90pub fn bind_listener(addr: &DaemonAddr) -> Result<NamedPipeListener> {
91    match addr {
92        DaemonAddr::NamedPipe(name) => NamedPipeListener::bind(name),
93    }
94}
95
96/// Connect to the daemon at the given address.
97#[cfg(windows)]
98pub async fn connect(
99    addr: &DaemonAddr,
100) -> Result<tokio::net::windows::named_pipe::NamedPipeClient> {
101    match addr {
102        DaemonAddr::NamedPipe(name) => windows::connect(name).await,
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109
110    #[test]
111    fn daemon_addr_display_non_empty() {
112        let addr = DaemonAddr::default_for_current_os();
113        let display = addr.display();
114        assert!(!display.is_empty());
115    }
116
117    #[test]
118    fn cleanup_nonexistent_does_not_panic() {
119        #[cfg(unix)]
120        {
121            let addr = DaemonAddr::Unix(std::path::PathBuf::from(
122                "/tmp/lean-ctx-test-nonexistent.sock",
123            ));
124            cleanup(&addr);
125        }
126    }
127}