use alloc::vec::Vec;
use core::cell::RefCell;
use embassy_futures::select::{select, select3};
use embassy_net::{IpCidr, udp::UdpSocket};
use embassy_sync::{blocking_mutex::raw::NoopRawMutex, signal::Signal};
use embassy_time::{Instant, Timer};
use hick_smoltcp::Engine;
use mdns_proto::{
CollectedAnswer, EndpointConfig, QueryHandle, QuerySpec, ServiceHandle, ServiceSpec,
error::{RegisterServiceError, StartQueryError},
event::{EndpointEvent, QueryUpdate, ServiceUpdate},
};
use rand_core::Rng;
use crate::{
io::{DualUdp, wait_either_recv},
time::EmbassyInstant,
};
pub struct MdnsState<R> {
engine: RefCell<Engine<EmbassyInstant, R>>,
wake: Signal<NoopRawMutex, ()>,
}
impl<R: Rng> MdnsState<R> {
pub fn new(config: EndpointConfig, rng: R) -> Self {
Self {
engine: RefCell::new(Engine::new(config, rng)),
wake: Signal::new(),
}
}
pub fn set_local_subnets(&self, subnets: Vec<IpCidr>) {
self.engine.borrow_mut().set_local_subnets(subnets);
}
pub fn register_service(&self, spec: ServiceSpec) -> Result<ServiceHandle, RegisterServiceError> {
let handle = self.engine.borrow_mut().register_service(spec, now())?;
self.wake.signal(());
Ok(handle)
}
pub fn unregister_service(&self, handle: ServiceHandle) {
self.engine.borrow_mut().unregister_service(handle, now());
self.wake.signal(());
}
pub fn start_query(&self, spec: QuerySpec) -> Result<QueryHandle, StartQueryError> {
let handle = self.engine.borrow_mut().start_query(spec, now())?;
self.wake.signal(());
Ok(handle)
}
pub fn cancel_query(&self, handle: QueryHandle) {
self.engine.borrow_mut().cancel_query(handle);
self.wake.signal(());
}
pub fn collected_answers(&self, handle: QueryHandle) -> Vec<CollectedAnswer> {
self
.engine
.borrow()
.collected_answers(handle)
.cloned()
.collect()
}
pub fn query_accepted_count(&self, handle: QueryHandle) -> Option<u64> {
self.engine.borrow().query_accepted_count(handle)
}
pub fn poll_service_update(&self, handle: ServiceHandle) -> Option<ServiceUpdate> {
self.engine.borrow_mut().poll_service_update(handle)
}
pub fn poll_query_update(&self, handle: QueryHandle) -> Option<QueryUpdate> {
self.engine.borrow_mut().poll_query_update(handle)
}
pub fn poll_endpoint_event(&self) -> Option<EndpointEvent> {
self.engine.borrow_mut().poll_endpoint_event()
}
#[cfg(feature = "stats")]
#[cfg_attr(docsrs, doc(cfg(feature = "stats")))]
pub fn stats(&self) -> hick_trace::stats::StatsSnapshot {
self.engine.borrow().stats()
}
pub async fn run(
&self,
mut v4: Option<&mut UdpSocket<'_>>,
mut v6: Option<&mut UdpSocket<'_>>,
scratch: &mut [u8],
) -> ! {
if let Some(s) = v4.as_deref_mut() {
s.set_hop_limit(Some(255));
#[cfg(feature = "defmt")]
defmt::debug!("hick-embassy MdnsState: v4 hop-limit set to 255 (RFC 6762 §11)");
}
if let Some(s) = v6.as_deref_mut() {
s.set_hop_limit(Some(255));
#[cfg(feature = "defmt")]
defmt::debug!("hick-embassy MdnsState: v6 hop-limit set to 255 (RFC 6762 §11)");
}
loop {
let deadline = {
let mut engine = self.engine.borrow_mut();
let mut io = DualUdp::new(v4.as_deref(), v6.as_deref());
engine.pump(now(), &mut io, scratch)
};
match deadline {
Some(d) => {
#[cfg(feature = "defmt")]
defmt::trace!("hick-embassy MdnsState: waiting for recv, deadline, or wake signal");
let _ = select3(
wait_either_recv(v4.as_deref(), v6.as_deref()),
Timer::at(d.0),
self.wake.wait(),
)
.await;
}
None => {
#[cfg(feature = "defmt")]
defmt::trace!("hick-embassy MdnsState: no deadline, waiting for recv or wake signal");
let _ = select(
wait_either_recv(v4.as_deref(), v6.as_deref()),
self.wake.wait(),
)
.await;
}
}
}
}
}
fn now() -> EmbassyInstant {
EmbassyInstant(Instant::now())
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests;