use core::net::{Ipv4Addr, Ipv6Addr};
use crate::domain::base::{iana::Class, Record, Ttl};
use crate::domain::rdata::{Aaaa, AllRecordData, Ptr, Srv, A};
use crate::{HostAnswer, HostAnswers, MdnsError, NameSlice, RecordDataChain, Txt, DNS_SD_OWNER};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Host<'a> {
pub hostname: &'a str,
pub ipv4: Ipv4Addr,
pub ipv6: Ipv6Addr,
#[cfg_attr(feature = "defmt", defmt(Debug2Format))]
pub ttl: Ttl,
}
impl Host<'_> {
fn visit_answers<F, E>(&self, mut f: F) -> Result<(), E>
where
F: FnMut(HostAnswer) -> Result<(), E>,
E: From<MdnsError>,
{
let owner = &[self.hostname, "local"];
if !self.ipv4.is_unspecified() {
f(Record::new(
NameSlice::new(owner),
Class::IN,
self.ttl,
RecordDataChain::Next(AllRecordData::A(A::new(domain::base::net::Ipv4Addr::from(
self.ipv4.octets(),
)))),
))?;
}
if !self.ipv6.is_unspecified() {
f(Record::new(
NameSlice::new(owner),
Class::IN,
self.ttl,
RecordDataChain::Next(AllRecordData::Aaaa(Aaaa::new(
domain::base::net::Ipv6Addr::from(self.ipv6.octets()),
))),
))?;
}
Ok(())
}
}
impl HostAnswers for Host<'_> {
fn visit<F, E>(&self, mut f: F) -> Result<(), E>
where
F: FnMut(HostAnswer) -> Result<(), E>,
E: From<MdnsError>,
{
self.visit_answers(&mut f)
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Service<'a> {
pub name: &'a str,
pub priority: u16,
pub weight: u16,
pub service: &'a str,
pub protocol: &'a str,
pub port: u16,
pub service_subtypes: &'a [&'a str],
pub txt_kvs: &'a [(&'a str, &'a str)],
}
impl Service<'_> {
fn visit_answers<F, E>(&self, host: &Host, mut f: F) -> Result<(), E>
where
F: FnMut(HostAnswer) -> Result<(), E>,
E: From<MdnsError>,
{
host.visit_answers(&mut f)?;
let owner = &[self.name, self.service, self.protocol, "local"];
let stype = &[self.service, self.protocol, "local"];
let target = &[host.hostname, "local"];
f(Record::new(
NameSlice::new(owner),
Class::IN,
host.ttl,
RecordDataChain::Next(AllRecordData::Srv(Srv::new(
self.priority,
self.weight,
self.port,
NameSlice::new(target),
))),
))?;
f(Record::new(
NameSlice::new(owner),
Class::IN,
host.ttl,
RecordDataChain::This(Txt::new(self.txt_kvs)),
))?;
f(Record::new(
DNS_SD_OWNER,
Class::IN,
host.ttl,
RecordDataChain::Next(AllRecordData::Ptr(Ptr::new(NameSlice::new(stype)))),
))?;
f(Record::new(
NameSlice::new(stype),
Class::IN,
host.ttl,
RecordDataChain::Next(AllRecordData::Ptr(Ptr::new(NameSlice::new(owner)))),
))?;
for subtype in self.service_subtypes {
let subtype_owner = &[subtype, self.name, self.service, self.protocol, "local"];
let subtype = &[subtype, "_sub", self.service, self.protocol, "local"];
f(Record::new(
NameSlice::new(subtype_owner),
Class::IN,
host.ttl,
RecordDataChain::Next(AllRecordData::Ptr(Ptr::new(NameSlice::new(owner)))),
))?;
f(Record::new(
NameSlice::new(subtype),
Class::IN,
host.ttl,
RecordDataChain::Next(AllRecordData::Ptr(Ptr::new(NameSlice::new(subtype_owner)))),
))?;
f(Record::new(
DNS_SD_OWNER,
Class::IN,
host.ttl,
RecordDataChain::Next(AllRecordData::Ptr(Ptr::new(NameSlice::new(subtype)))),
))?;
}
Ok(())
}
}
pub struct ServiceAnswers<'a> {
host: &'a Host<'a>,
service: &'a Service<'a>,
}
impl<'a> ServiceAnswers<'a> {
pub const fn new(host: &'a Host<'a>, service: &'a Service<'a>) -> Self {
Self { host, service }
}
}
impl HostAnswers for ServiceAnswers<'_> {
fn visit<F, E>(&self, mut f: F) -> Result<(), E>
where
F: FnMut(HostAnswer) -> Result<(), E>,
E: From<MdnsError>,
{
self.service.visit_answers(self.host, &mut f)
}
}