WIP: functional checkpoint!
This commit is contained in:
1 parent
7dd717a887
commit
b2e4bb0476
1 file changed
+86
@@ -0,0 +1,86 @@
|
||||
# Add non-blocking `connect` variant
|
||||
|
||||
## Problem
|
||||
|
||||
`connect(None, timeout)` in `src/connect.rs` has a three-step fallback:
|
||||
|
||||
1. `DONGLORA_MUX_TCP` env var -> TCP mux
|
||||
2. Unix socket mux (if socket file exists)
|
||||
3. Direct USB serial via `find_port().unwrap_or_else(wait_for_device)`
|
||||
|
||||
Step 3 calls `wait_for_device()` which polls `find_port()` every 500ms
|
||||
**indefinitely** until a USB device appears. This makes `connect(None, ...)`
|
||||
unsuitable for callers that need to retry with backoff or report disconnected
|
||||
status (e.g. donglora-bridge's reconnect loop).
|
||||
|
||||
Today, donglora-bridge works around this by manually reimplementing the
|
||||
fallback chain:
|
||||
|
||||
```rust
|
||||
if let Ok(c) = connect_mux_auto(timeout) {
|
||||
(c, "mux".to_string())
|
||||
} else {
|
||||
let port_path = find_port()
|
||||
.ok_or_else(|| anyhow!("no mux or USB dongle found"))?;
|
||||
let c = connect(Some(&port_path), timeout)?;
|
||||
(c, shorten_path(&port_path))
|
||||
}
|
||||
```
|
||||
|
||||
This duplicates logic from `connect()` (steps 1-2 via `connect_mux_auto`, step
|
||||
3 minus the blocking wait) and won't pick up any future changes to the
|
||||
`connect()` fallback order.
|
||||
|
||||
## Proposed solution
|
||||
|
||||
Add a `try_connect` (or similar) that runs the same mux -> serial fallback as
|
||||
`connect(None, timeout)` but replaces `wait_for_device()` with a single
|
||||
`find_port()` call, returning an error immediately if no device is found:
|
||||
|
||||
```rust
|
||||
/// Like [`connect`] but returns an error instead of blocking when no USB
|
||||
/// device is present. Suitable for callers with their own retry logic.
|
||||
pub fn try_connect(timeout: Duration) -> Result<Client<AnyTransport>> {
|
||||
// Steps 1-2: identical to connect() — try TCP mux, then Unix socket mux
|
||||
|
||||
// Step 3: non-blocking USB scan
|
||||
let port_path = discovery::find_port()
|
||||
.ok_or_else(|| anyhow!("no DongLoRa device found (no mux, no USB device)"))?;
|
||||
let transport = SerialTransport::open(&port_path, timeout)?;
|
||||
Ok(Client::new(AnyTransport::Serial(transport)))
|
||||
}
|
||||
```
|
||||
|
||||
This would let donglora-bridge collapse its connection code to:
|
||||
|
||||
```rust
|
||||
let (mut client, device) = if let Some(port) = port {
|
||||
let c = connect(Some(port), timeout)?;
|
||||
(c, shorten_path(port))
|
||||
} else {
|
||||
let c = try_connect(timeout)?;
|
||||
let device = match c.transport() {
|
||||
AnyTransport::Mux(_) => "mux".to_string(),
|
||||
AnyTransport::Serial(_) => find_port()
|
||||
.map(|p| shorten_path(&p))
|
||||
.unwrap_or_else(|| "serial".to_string()),
|
||||
};
|
||||
(c, device)
|
||||
};
|
||||
```
|
||||
|
||||
### Device name from transport
|
||||
|
||||
The bridge also needs to know *what* it connected to for display purposes. Two
|
||||
options:
|
||||
|
||||
1. Match on `AnyTransport` variant after connecting (shown above) -- works but
|
||||
requires a redundant `find_port()` call for the display name since
|
||||
`SerialTransport` has private fields.
|
||||
2. Have `try_connect` return the port path alongside the client, e.g.
|
||||
`-> Result<(Client<AnyTransport>, ConnectionInfo)>` where `ConnectionInfo`
|
||||
is an enum like `Mux { socket_path }` | `Serial { port_path }`. Cleaner
|
||||
but a larger API change.
|
||||
|
||||
Either works. Option 2 would also benefit `connect()` callers who want to log
|
||||
what they connected to.
|
||||
Reference in new issue
Block a user