Skip to main content

lean_ctx/ipc/
mod.rs

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