use futures_util::FutureExt;
use std::{
future::Future,
io,
os::raw::c_void,
pin::Pin,
task::{
Context,
Poll,
},
};
use crate::{
cstr,
dns_consts::{
Class,
Type,
},
ffi,
inner,
interface::Interface,
};
type CallbackFuture = crate::future::ServiceFuture<inner::SharedService, RegisterRecordResult>;
pub struct Connection(inner::SharedService);
#[doc(alias = "DNSServiceCreateConnection")]
pub fn connect() -> io::Result<Connection> {
crate::init();
Ok(Connection(inner::SharedService::create_connection()?))
}
bitflags::bitflags! {
#[derive(Default)]
pub struct RegisterRecordFlags: ffi::DNSServiceFlags {
const SHARED = ffi::FLAGS_SHARED;
const UNIQUE = ffi::FLAGS_UNIQUE;
}
}
#[must_use = "futures do nothing unless polled"]
pub struct RegisterRecord {
future: CallbackFuture,
record: Option<crate::Record>,
}
impl RegisterRecord {
pin_utils::unsafe_pinned!(future: CallbackFuture);
pin_utils::unsafe_unpinned!(record: Option<crate::Record>);
}
impl Future for RegisterRecord {
type Output = io::Result<crate::Record>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
futures_core::ready!(self.as_mut().future().poll(cx))?;
Poll::Ready(Ok(self.record().take().unwrap()))
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
struct RegisterRecordResult;
unsafe extern "C" fn register_record_callback(
_sd_ref: ffi::DNSServiceRef,
_record_ref: ffi::DNSRecordRef,
_flags: ffi::DNSServiceFlags,
error_code: ffi::DNSServiceErrorType,
context: *mut c_void,
) {
CallbackFuture::run_callback(context, error_code, || Ok(RegisterRecordResult));
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct RegisterRecordData {
pub flags: RegisterRecordFlags,
pub interface: Interface,
pub rr_class: Class,
pub ttl: u32,
#[doc(hidden)]
pub _non_exhaustive: crate::non_exhaustive_struct::NonExhaustiveMarker,
}
impl Default for RegisterRecordData {
fn default() -> Self {
Self {
flags: RegisterRecordFlags::default(),
interface: Interface::default(),
rr_class: Class::IN,
ttl: 0,
_non_exhaustive: crate::non_exhaustive_struct::NonExhaustiveMarker,
}
}
}
impl Connection {
#[doc(alias = "DNSServiceRegisterRecord")]
pub fn register_record_extended(
&self,
fullname: &str,
rr_type: Type,
rdata: &[u8],
data: RegisterRecordData,
) -> io::Result<RegisterRecord> {
let fullname = cstr::CStr::from(&fullname)?;
let (future, record) = CallbackFuture::new_with(self.0.clone(), move |sender| {
self.0.clone().register_record(
data.flags.bits(),
data.interface.into_raw(),
&fullname,
rr_type,
data.rr_class,
rdata,
data.ttl,
Some(register_record_callback),
sender,
)
})?;
Ok(RegisterRecord {
future,
record: Some(record.into()),
})
}
#[doc(alias = "DNSServiceRegisterRecord")]
pub fn register_record(
&self,
fullname: &str,
rr_type: Type,
rdata: &[u8],
) -> io::Result<RegisterRecord> {
self.register_record_extended(fullname, rr_type, rdata, RegisterRecordData::default())
}
}
impl RegisterRecord {
fn inner_record(&self) -> &crate::Record {
self.record.as_ref().expect("RegisterRecord future is done")
}
pub fn rr_type(&self) -> Type {
self.inner_record().rr_type()
}
#[doc(alias = "DNSServiceUpdateRecord")]
pub fn update_record(&self, rdata: &[u8], ttl: u32) -> io::Result<()> {
self.inner_record().update_record(rdata, ttl)
}
pub fn keep(self) {
let (fut, rec) = (
self.future,
self.record.expect("RegisterRecord future is done"),
);
tokio::spawn(fut.map(|_| ()));
rec.keep();
}
}