hylarana_discovery/
lib.rs1use std::{fmt::Debug, net::Ipv4Addr, thread};
2
3use mdns_sd::{IfKind, ServiceDaemon, ServiceEvent, ServiceInfo};
4use serde::{de::DeserializeOwned, Serialize};
5use thiserror::Error;
6use uuid::Uuid;
7
8#[derive(Debug, Error)]
9pub enum DiscoveryError {
10 #[error(transparent)]
11 MdnsError(#[from] mdns_sd::Error),
12 #[error(transparent)]
13 JsonError(#[from] serde_json::Error),
14}
15
16pub struct DiscoveryService(ServiceDaemon);
21
22impl DiscoveryService {
23 pub fn register<P: Serialize + Debug>(
27 port: u16,
28 properties: &P,
29 ) -> Result<Self, DiscoveryError> {
30 let mdns = ServiceDaemon::new()?;
31 mdns.disable_interface(IfKind::IPv6)?;
32
33 let id = Uuid::new_v4().to_string();
34 mdns.register(
35 ServiceInfo::new(
36 "_hylarana._udp.local.",
37 "sender",
38 &format!("{}._hylarana._udp.local.", id),
39 "",
40 port,
41 &[("p", &serde_json::to_string(properties)?)][..],
42 )?
43 .enable_addr_auto(),
44 )?;
45
46 log::info!(
47 "discovery service register sender, port={}, id={}, properties={:?}",
48 port,
49 id,
50 properties
51 );
52
53 Ok(Self(mdns))
54 }
55
56 pub fn query<P: DeserializeOwned + Debug, T: Fn(Vec<Ipv4Addr>, P) + Send + 'static>(
60 func: T,
61 ) -> Result<Self, DiscoveryError> {
62 let mdns = ServiceDaemon::new()?;
63 mdns.disable_interface(IfKind::IPv6)?;
64
65 let receiver = mdns.browse("_hylarana._udp.local.")?;
66 thread::spawn(move || {
67 let process = |info: ServiceInfo| {
68 if let Some(properties) = info.get_property("p") {
69 let properties = serde_json::from_str(properties.val_str())?;
70 let addrs = info
71 .get_addresses_v4()
72 .into_iter()
73 .map(|it| *it)
74 .collect::<Vec<_>>();
75
76 log::info!(
77 "discovery service query a sender, host={}, address={:?}, properties={:?}",
78 info.get_hostname(),
79 addrs,
80 properties,
81 );
82
83 func(addrs, properties);
84 }
85
86 Ok::<(), DiscoveryError>(())
87 };
88
89 loop {
90 match receiver.recv() {
91 Ok(ServiceEvent::ServiceResolved(info)) => {
92 if info.get_fullname() == "sender._hylarana._udp.local." {
93 if let Err(e) = process(info) {
94 log::warn!("discovery service resolved error={:?}", e);
95 }
96 }
97 }
98 Err(e) => {
99 log::error!("discovery service query error={:?}", e);
100
101 break;
102 }
103 Ok(event) => {
104 log::info!("discovery service query event={:?}", event);
105 }
106 }
107 }
108 });
109
110 Ok(Self(mdns))
111 }
112}
113
114impl Drop for DiscoveryService {
115 fn drop(&mut self) {
116 let _ = self.0.unregister("_hylarana._udp.local.");
117 let _ = self.0.stop_browse("_hylarana._udp.local.");
118 }
119}