Skip to main content

snapcast_client/
discovery.rs

1//! mDNS/ZeroConf service discovery for finding snapservers.
2
3use std::time::Duration;
4
5use anyhow::{Result, bail};
6
7/// Discovered snapserver endpoint.
8#[derive(Debug, Clone)]
9pub struct Endpoint {
10    /// Hostname or IP address.
11    pub host: String,
12    /// TCP port number.
13    pub port: u16,
14}
15
16/// Browse for a snapserver via mDNS. Returns the first resolved endpoint.
17pub async fn discover(timeout: Duration, service_type: &str) -> Result<Endpoint> {
18    let mdns = mdns_sd::ServiceDaemon::new()?;
19    let receiver = mdns.browse(service_type)?;
20
21    let deadline = tokio::time::Instant::now() + timeout;
22
23    loop {
24        let remaining = deadline.saturating_duration_since(tokio::time::Instant::now());
25        if remaining.is_zero() {
26            mdns.stop_browse(service_type).ok();
27            bail!("mDNS discovery timed out after {timeout:?}");
28        }
29
30        match tokio::time::timeout(remaining, async { receiver.recv_async().await }).await {
31            Ok(Ok(mdns_sd::ServiceEvent::ServiceResolved(info))) => {
32                let host = info
33                    .get_addresses()
34                    .iter()
35                    .next()
36                    .map(|a| a.to_string())
37                    .unwrap_or_else(|| info.get_hostname().trim_end_matches('.').to_string());
38                let port = info.get_port();
39                tracing::info!(host = %host, port, "Discovered snapserver via mDNS");
40                mdns.stop_browse(service_type).ok();
41                return Ok(Endpoint { host, port });
42            }
43            Ok(Ok(_)) => continue, // other events, keep waiting
44            Ok(Err(e)) => {
45                mdns.stop_browse(service_type).ok();
46                bail!("mDNS receiver error: {e}");
47            }
48            Err(_) => {
49                mdns.stop_browse(service_type).ok();
50                bail!("mDNS discovery timed out after {timeout:?}");
51            }
52        }
53    }
54}