donglora_client/
connect.rs1use std::time::Duration;
8
9use tracing::debug;
10
11use crate::client::Client;
12use crate::discovery;
13use crate::transport::{AnyTransport, MuxTransport, SerialTransport};
14
15const DEFAULT_TIMEOUT: Duration = Duration::from_secs(2);
17
18pub fn default_socket_path() -> String {
24 if let Ok(env) = std::env::var("DONGLORA_MUX") {
25 return env;
26 }
27 if let Ok(xdg) = std::env::var("XDG_RUNTIME_DIR") {
28 return format!("{xdg}/donglora/mux.sock");
29 }
30 "/tmp/donglora-mux.sock".to_string()
31}
32
33fn find_mux_socket() -> Option<String> {
35 if let Ok(env) = std::env::var("DONGLORA_MUX") {
36 if std::path::Path::new(&env).exists() {
37 return Some(env);
38 }
39 return None;
40 }
41 if let Ok(xdg) = std::env::var("XDG_RUNTIME_DIR") {
42 let p = format!("{xdg}/donglora/mux.sock");
43 if std::path::Path::new(&p).exists() {
44 return Some(p);
45 }
46 }
47 let p = "/tmp/donglora-mux.sock";
48 if std::path::Path::new(p).exists() {
49 return Some(p.to_string());
50 }
51 None
52}
53
54#[cfg(unix)]
56pub fn mux_connect(path: Option<&str>, timeout: Duration) -> anyhow::Result<Client<MuxTransport>> {
57 let path = match path {
58 Some(p) => p.to_string(),
59 None => find_mux_socket().ok_or_else(|| anyhow::anyhow!("no mux socket found"))?,
60 };
61 let transport = MuxTransport::unix(&path, timeout)?;
62 let mut client = Client::new(transport);
63 client.validate()?;
64 Ok(client)
65}
66
67pub fn mux_tcp_connect(host: &str, port: u16, timeout: Duration) -> anyhow::Result<Client<MuxTransport>> {
69 let transport = MuxTransport::tcp(host, port, timeout)?;
70 let mut client = Client::new(transport);
71 client.validate()?;
72 Ok(client)
73}
74
75pub fn connect(port: Option<&str>, timeout: Duration) -> anyhow::Result<Client<AnyTransport>> {
84 if let Some(port) = port {
86 debug!("opening serial port {port}");
87 let transport = SerialTransport::open(port, timeout)?;
88 let mut client = Client::new(AnyTransport::Serial(transport));
89 client.validate()?;
90 return Ok(client);
91 }
92
93 if let Ok(tcp) = std::env::var("DONGLORA_MUX_TCP")
95 && let Some(transport) = try_tcp_mux(&tcp, timeout)
96 {
97 debug!("connected to TCP mux at {tcp}");
98 let mut client = Client::new(AnyTransport::Mux(transport));
99 client.validate()?;
100 return Ok(client);
101 }
102
103 #[cfg(unix)]
105 if let Some(path) = find_mux_socket() {
106 match MuxTransport::unix(&path, timeout) {
107 Ok(transport) => {
108 debug!("connected to mux socket at {path}");
109 let mut client = Client::new(AnyTransport::Mux(transport));
110 if client.validate().is_ok() {
111 return Ok(client);
112 }
113 debug!("mux at {path} did not validate, falling back to USB");
114 }
115 Err(_) => {
116 debug!("mux socket at {path} not reachable, falling back to USB");
118 }
119 }
120 }
121
122 let port_path = discovery::find_port().unwrap_or_else(discovery::wait_for_device);
124 debug!("opening serial port {port_path}");
125 let transport = SerialTransport::open(&port_path, timeout)?;
126 let mut client = Client::new(AnyTransport::Serial(transport));
127 client.validate()?;
128 Ok(client)
129}
130
131pub fn connect_default() -> anyhow::Result<Client<AnyTransport>> {
133 connect(None, DEFAULT_TIMEOUT)
134}
135
136pub fn connect_mux_auto(timeout: Duration) -> anyhow::Result<Client<AnyTransport>> {
144 if let Ok(tcp) = std::env::var("DONGLORA_MUX_TCP")
146 && let Some(transport) = try_tcp_mux(&tcp, timeout)
147 {
148 debug!("connected to TCP mux at {tcp}");
149 let mut client = Client::new(AnyTransport::Mux(transport));
150 client.validate()?;
151 return Ok(client);
152 }
153
154 #[cfg(unix)]
156 {
157 let path = find_mux_socket().ok_or_else(|| anyhow::anyhow!("no mux socket found"))?;
158 let transport = MuxTransport::unix(&path, timeout)?;
159 debug!("connected to mux socket at {path}");
160 let mut client = Client::new(AnyTransport::Mux(transport));
161 client.validate()?;
162 Ok(client)
163 }
164
165 #[cfg(not(unix))]
166 anyhow::bail!("mux-only mode requires Unix socket support or DONGLORA_MUX_TCP")
167}
168
169pub fn try_connect(timeout: Duration) -> anyhow::Result<Client<AnyTransport>> {
176 if let Ok(tcp) = std::env::var("DONGLORA_MUX_TCP")
178 && let Some(transport) = try_tcp_mux(&tcp, timeout)
179 {
180 debug!("connected to TCP mux at {tcp}");
181 let mut client = Client::new(AnyTransport::Mux(transport));
182 client.validate()?;
183 return Ok(client);
184 }
185
186 #[cfg(unix)]
188 if let Some(path) = find_mux_socket() {
189 match MuxTransport::unix(&path, timeout) {
190 Ok(transport) => {
191 debug!("connected to mux socket at {path}");
192 let mut client = Client::new(AnyTransport::Mux(transport));
193 if client.validate().is_ok() {
194 return Ok(client);
195 }
196 debug!("mux socket at {path} did not validate, falling back to USB");
197 }
198 Err(_) => {
199 debug!("mux socket at {path} not reachable, falling back to USB");
200 }
201 }
202 }
203
204 let port_path =
206 discovery::find_port().ok_or_else(|| anyhow::anyhow!("no DongLoRa device found (no mux, no USB device)"))?;
207 debug!("opening serial port {port_path}");
208 let transport = SerialTransport::open(&port_path, timeout)?;
209 let mut client = Client::new(AnyTransport::Serial(transport));
210 client.validate()?;
211 Ok(client)
212}
213
214fn try_tcp_mux(addr: &str, timeout: Duration) -> Option<MuxTransport> {
215 let (host, port) = if let Some((h, p)) = addr.rsplit_once(':') {
216 let host = if h.is_empty() { "localhost" } else { h };
217 let port: u16 = p.parse().ok()?;
218 (host.to_string(), port)
219 } else {
220 let port: u16 = addr.parse().ok()?;
221 ("localhost".to_string(), port)
222 };
223 MuxTransport::tcp(&host, port, timeout).ok()
224}