Signed-off-by: Walker Crouse <walker.crouse@coop.co.uk>
This commit is contained in:
Walker Crouse 2020-09-28 22:11:30 -04:00
parent 0881c308bd
commit e0fea5b812
13 changed files with 115 additions and 95 deletions

View File

@ -83,6 +83,7 @@ use std::any::Any;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use zeroconf::{MdnsBrowser, ServiceDiscovery}; use zeroconf::{MdnsBrowser, ServiceDiscovery};
use zeroconf::prelude::*;
fn main() { fn main() {
let mut browser = MdnsBrowser::new("_http._tcp"); let mut browser = MdnsBrowser::new("_http._tcp");

View File

@ -2,6 +2,7 @@ use std::any::Any;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use zeroconf::{MdnsBrowser, ServiceDiscovery}; use zeroconf::{MdnsBrowser, ServiceDiscovery};
use zeroconf::prelude::*;
fn main() { fn main() {
let mut browser = MdnsBrowser::new("_http._tcp"); let mut browser = MdnsBrowser::new("_http._tcp");

29
zeroconf/src/browser.rs Normal file
View File

@ -0,0 +1,29 @@
use crate::{EventLoop, NetworkInterface, Result, ServiceDiscoveredCallback};
use std::any::Any;
pub trait TMdnsBrowser {
/// Creates a new `MdnsBrowser` that browses for the specified `kind` (e.g. `_http._tcp`)
fn new(kind: &str) -> Self;
/// Sets the network interface on which to browse for services on.
///
/// Most applications will want to use the default value `NetworkInterface::Unspec` to browse
/// on all available interfaces.
fn set_network_interface(&mut self, interface: NetworkInterface);
/// Sets the [`ServiceDiscoveredCallback`] that is invoked when the browser has discovered and
/// resolved a service.
///
/// [`ServiceDiscoveredCallback`]: ../type.ServiceDiscoveredCallback.html
fn set_service_discovered_callback(
&mut self,
service_discovered_callback: Box<ServiceDiscoveredCallback>,
);
/// Sets the optional user context to pass through to the callback. This is useful if you need
/// to share state between pre and post-callback. The context type must implement `Any`.
fn set_context(&mut self, context: Box<dyn Any>);
/// Starts the browser. Returns an `EventLoop` which can be called to keep the browser alive.
fn browse_services(&mut self) -> Result<EventLoop>;
}

View File

@ -22,50 +22,50 @@
//! use std::time::Duration; //! use std::time::Duration;
//! use zeroconf::{MdnsService, ServiceRegistration, TxtRecord}; //! use zeroconf::{MdnsService, ServiceRegistration, TxtRecord};
//! use zeroconf::prelude::*; //! use zeroconf::prelude::*;
//! //!
//! #[derive(Default, Debug)] //! #[derive(Default, Debug)]
//! pub struct Context { //! pub struct Context {
//! service_name: String, //! service_name: String,
//! } //! }
//! //!
//! fn main() { //! fn main() {
//! let mut service = MdnsService::new("_http._tcp", 8080); //! let mut service = MdnsService::new("_http._tcp", 8080);
//! let mut txt_record = TxtRecord::new(); //! let mut txt_record = TxtRecord::new();
//! let context: Arc<Mutex<Context>> = Arc::default(); //! let context: Arc<Mutex<Context>> = Arc::default();
//! //!
//! txt_record.insert("foo", "bar").unwrap(); //! txt_record.insert("foo", "bar").unwrap();
//! //!
//! service.set_registered_callback(Box::new(on_service_registered)); //! service.set_registered_callback(Box::new(on_service_registered));
//! service.set_context(Box::new(context)); //! service.set_context(Box::new(context));
//! service.set_txt_record(txt_record); //! service.set_txt_record(txt_record);
//! //!
//! let event_loop = service.register().unwrap(); //! let event_loop = service.register().unwrap();
//! //!
//! loop { //! loop {
//! // calling `poll()` will keep this service alive //! // calling `poll()` will keep this service alive
//! event_loop.poll(Duration::from_secs(0)).unwrap(); //! event_loop.poll(Duration::from_secs(0)).unwrap();
//! } //! }
//! } //! }
//! //!
//! fn on_service_registered( //! fn on_service_registered(
//! result: zeroconf::Result<ServiceRegistration>, //! result: zeroconf::Result<ServiceRegistration>,
//! context: Option<Arc<dyn Any>>, //! context: Option<Arc<dyn Any>>,
//! ) { //! ) {
//! let service = result.unwrap(); //! let service = result.unwrap();
//! //!
//! println!("Service registered: {:?}", service); //! println!("Service registered: {:?}", service);
//! //!
//! let context = context //! let context = context
//! .as_ref() //! .as_ref()
//! .unwrap() //! .unwrap()
//! .downcast_ref::<Arc<Mutex<Context>>>() //! .downcast_ref::<Arc<Mutex<Context>>>()
//! .unwrap() //! .unwrap()
//! .clone(); //! .clone();
//! //!
//! context.lock().unwrap().service_name = service.name().clone(); //! context.lock().unwrap().service_name = service.name().clone();
//! //!
//! println!("Context: {:?}", context); //! println!("Context: {:?}", context);
//! //!
//! // ... //! // ...
//! } //! }
//! ``` //! ```
@ -76,6 +76,7 @@
//! use std::sync::Arc; //! use std::sync::Arc;
//! use std::time::Duration; //! use std::time::Duration;
//! use zeroconf::{MdnsBrowser, ServiceDiscovery}; //! use zeroconf::{MdnsBrowser, ServiceDiscovery};
//! use zeroconf::prelude::*;
//! //!
//! fn main() { //! fn main() {
//! let mut browser = MdnsBrowser::new("_http._tcp"); //! let mut browser = MdnsBrowser::new("_http._tcp");
@ -138,10 +139,12 @@ mod interface;
mod tests; mod tests;
mod txt_record; mod txt_record;
pub mod browser;
pub mod builder; pub mod builder;
pub mod error; pub mod error;
pub mod ffi; pub mod ffi;
pub mod prelude; pub mod prelude;
pub mod service;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub mod linux; pub mod linux;

View File

@ -6,6 +6,7 @@ use super::raw_browser::{ManagedAvahiServiceBrowser, ManagedAvahiServiceBrowserP
use super::resolver::{ use super::resolver::{
ManagedAvahiServiceResolver, ManagedAvahiServiceResolverParams, ServiceResolverSet, ManagedAvahiServiceResolver, ManagedAvahiServiceResolverParams, ServiceResolverSet,
}; };
use crate::browser::TMdnsBrowser;
use crate::builder::BuilderDelegate; use crate::builder::BuilderDelegate;
use crate::ffi::{c_str, AsRaw, FromRaw}; use crate::ffi::{c_str, AsRaw, FromRaw};
use crate::Result; use crate::Result;
@ -32,9 +33,8 @@ pub struct AvahiMdnsBrowser {
context: *mut AvahiBrowserContext, context: *mut AvahiBrowserContext,
} }
impl AvahiMdnsBrowser { impl TMdnsBrowser for AvahiMdnsBrowser {
/// Creates a new `AvahiMdnsBrowser` that browses for the specified `kind` (e.g. `_http._tcp`) fn new(kind: &str) -> Self {
pub fn new(kind: &str) -> Self {
Self { Self {
client: None, client: None,
poll: None, poll: None,
@ -45,33 +45,22 @@ impl AvahiMdnsBrowser {
} }
} }
/// Sets the network interface on which to browse for services on. fn set_network_interface(&mut self, interface: NetworkInterface) {
///
/// Most applications will want to use the default value `NetworkInterface::Unspec` to browse
/// on all available interfaces.
pub fn set_network_interface(&mut self, interface: NetworkInterface) {
self.interface_index = avahi_util::interface_index(interface); self.interface_index = avahi_util::interface_index(interface);
} }
/// Sets the [`ServiceDiscoveredCallback`] that is invoked when the browser has discovered and fn set_service_discovered_callback(
/// resolved a service.
///
/// [`ServiceDiscoveredCallback`]: ../type.ServiceDiscoveredCallback.html
pub fn set_service_discovered_callback(
&mut self, &mut self,
service_discovered_callback: Box<ServiceDiscoveredCallback>, service_discovered_callback: Box<ServiceDiscoveredCallback>,
) { ) {
unsafe { (*self.context).service_discovered_callback = Some(service_discovered_callback) }; unsafe { (*self.context).service_discovered_callback = Some(service_discovered_callback) };
} }
/// Sets the optional user context to pass through to the callback. This is useful if you need fn set_context(&mut self, context: Box<dyn Any>) {
/// to share state between pre and post-callback. The context type must implement `Any`.
pub fn set_context(&mut self, context: Box<dyn Any>) {
unsafe { (*self.context).user_context = Some(Arc::from(context)) }; unsafe { (*self.context).user_context = Some(Arc::from(context)) };
} }
/// Starts the browser. Returns an `EventLoop` which can be called to keep the browser alive. fn browse_services(&mut self) -> Result<EventLoop> {
pub fn browse_services(&mut self) -> Result<EventLoop> {
debug!("Browsing services: {:?}", self); debug!("Browsing services: {:?}", self);
self.poll = Some(Arc::new(ManagedAvahiSimplePoll::new()?)); self.poll = Some(Arc::new(ManagedAvahiSimplePoll::new()?));

View File

@ -29,7 +29,6 @@ pub struct AvahiMdnsService {
} }
impl AvahiMdnsService { impl AvahiMdnsService {
/// Creates a new `AvahiMdnsService` with the specified `kind` (e.g. `_http._tcp`) and `port`.
pub fn new(kind: &str, port: u16) -> Self { pub fn new(kind: &str, port: u16) -> Self {
Self { Self {
client: None, client: None,
@ -49,51 +48,30 @@ impl AvahiMdnsService {
unsafe { (*self.context).name = Some(c_string!(name)) }; unsafe { (*self.context).name = Some(c_string!(name)) };
} }
/// Sets the network interface to bind this service to.
///
/// Most applications will want to use the default value `NetworkInterface::Unspec` to bind to
/// all available interfaces.
pub fn set_network_interface(&mut self, interface: NetworkInterface) { pub fn set_network_interface(&mut self, interface: NetworkInterface) {
unsafe { (*self.context).interface_index = avahi_util::interface_index(interface) }; unsafe { (*self.context).interface_index = avahi_util::interface_index(interface) };
} }
/// Sets the domain on which to advertise the service.
///
/// Most applications will want to use the default value of `ptr::null()` to register to the
/// default domain.
pub fn set_domain(&mut self, _domain: &str) { pub fn set_domain(&mut self, _domain: &str) {
todo!() todo!()
} }
/// Sets the SRV target host name.
///
/// Most applications will want to use the default value of `ptr::null()` to use the machine's
// default host name.
pub fn set_host(&mut self, _host: &str) { pub fn set_host(&mut self, _host: &str) {
todo!() todo!()
} }
/// Sets the optional `TxtRecord` to register this service with.
pub fn set_txt_record(&mut self, txt_record: TxtRecord) { pub fn set_txt_record(&mut self, txt_record: TxtRecord) {
self.txt_record = Some(txt_record); self.txt_record = Some(txt_record);
} }
/// Sets the [`ServiceRegisteredCallback`] that is invoked when the service has been
/// registered.
///
/// [`ServiceRegisteredCallback`]: ../type.ServiceRegisteredCallback.html
pub fn set_registered_callback(&mut self, registered_callback: Box<ServiceRegisteredCallback>) { pub fn set_registered_callback(&mut self, registered_callback: Box<ServiceRegisteredCallback>) {
unsafe { (*self.context).registered_callback = Some(registered_callback) }; unsafe { (*self.context).registered_callback = Some(registered_callback) };
} }
/// Sets the optional user context to pass through to the callback. This is useful if you need
/// to share state between pre and post-callback. The context type must implement `Any`.
pub fn set_context(&mut self, context: Box<dyn Any>) { pub fn set_context(&mut self, context: Box<dyn Any>) {
unsafe { (*self.context).user_context = Some(Arc::from(context)) }; unsafe { (*self.context).user_context = Some(Arc::from(context)) };
} }
/// Registers and start's the service. Returns an `EventLoop` which can be called to keep
/// the service alive.
pub fn register(&mut self) -> Result<EventLoop> { pub fn register(&mut self) -> Result<EventLoop> {
debug!("Registering service: {:?}", self); debug!("Registering service: {:?}", self);

View File

@ -8,6 +8,7 @@ use std::cell::UnsafeCell;
pub struct AvahiTxtRecord(UnsafeCell<ManagedAvahiStringList>); pub struct AvahiTxtRecord(UnsafeCell<ManagedAvahiStringList>);
impl AvahiTxtRecord { impl AvahiTxtRecord {
#[allow(clippy::mut_from_ref)]
fn inner(&self) -> &mut ManagedAvahiStringList { fn inner(&self) -> &mut ManagedAvahiStringList {
unsafe { &mut *self.0.get() } unsafe { &mut *self.0.get() }
} }
@ -98,11 +99,7 @@ impl Iterator for Iter<'_> {
type Item = (String, String); type Item = (String, String);
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
if self.node.is_none() { let mut n = self.node.take()?;
return None;
}
let mut n = self.node.take().unwrap();
let pair = n.get_pair(); let pair = n.get_pair();
self.node = n.next(); self.node = n.next();

View File

@ -3,6 +3,7 @@ use super::{bonjour_util, constants};
use crate::builder::BuilderDelegate; use crate::builder::BuilderDelegate;
use crate::ffi::c_str::{self, AsCChars}; use crate::ffi::c_str::{self, AsCChars};
use crate::ffi::{FromRaw, UnwrapOrNull}; use crate::ffi::{FromRaw, UnwrapOrNull};
use crate::service::TMdnsService;
use crate::{ use crate::{
EventLoop, NetworkInterface, Result, ServiceRegisteredCallback, ServiceRegistration, TxtRecord, EventLoop, NetworkInterface, Result, ServiceRegisteredCallback, ServiceRegistration, TxtRecord,
}; };
@ -26,10 +27,8 @@ pub struct BonjourMdnsService {
context: *mut BonjourServiceContext, context: *mut BonjourServiceContext,
} }
impl BonjourMdnsService { impl TMdnsService for BonjourMdnsService {
/// Creates a new `BonjourMdnsService` with the specified `kind` (e.g. `_http._tcp`) and fn new(kind: &str, port: u16) -> Self {
/// `port`.
pub fn new(kind: &str, port: u16) -> Self {
Self { Self {
service: Arc::default(), service: Arc::default(),
kind: c_string!(kind), kind: c_string!(kind),
@ -45,56 +44,35 @@ impl BonjourMdnsService {
/// Sets the name to register this service under. If no name is set, Bonjour will /// Sets the name to register this service under. If no name is set, Bonjour will
/// automatically assign one (usually to the name of the machine). /// automatically assign one (usually to the name of the machine).
pub fn set_name(&mut self, name: &str) { fn set_name(&mut self, name: &str) {
self.name = Some(c_string!(name)); self.name = Some(c_string!(name));
} }
/// Sets the network interface to bind this service to. fn set_network_interface(&mut self, interface: NetworkInterface) {
///
/// Most applications will want to use the default value `NetworkInterface::Unspec` to bind to
/// all available interfaces.
pub fn set_network_interface(&mut self, interface: NetworkInterface) {
self.interface_index = bonjour_util::interface_index(interface); self.interface_index = bonjour_util::interface_index(interface);
} }
/// Sets the domain on which to advertise the service. fn set_domain(&mut self, domain: &str) {
///
/// Most applications will want to use the default value of `ptr::null()` to register to the
/// default domain.
pub fn set_domain(&mut self, domain: &str) {
self.domain = Some(c_string!(domain)); self.domain = Some(c_string!(domain));
} }
/// Sets the SRV target host name. fn set_host(&mut self, host: &str) {
///
/// Most applications will want to use the default value of `ptr::null()` to use the machine's
// default host name.
pub fn set_host(&mut self, host: &str) {
self.host = Some(c_string!(host)); self.host = Some(c_string!(host));
} }
/// Sets the optional `TxtRecord` to register this service with. fn set_txt_record(&mut self, txt_record: TxtRecord) {
pub fn set_txt_record(&mut self, txt_record: TxtRecord) {
self.txt_record = Some(txt_record); self.txt_record = Some(txt_record);
} }
/// Sets the [`ServiceRegisteredCallback`] that is invoked when the service has been fn set_registered_callback(&mut self, registered_callback: Box<ServiceRegisteredCallback>) {
/// registered.
///
/// [`ServiceRegisteredCallback`]: ../type.ServiceRegisteredCallback.html
pub fn set_registered_callback(&mut self, registered_callback: Box<ServiceRegisteredCallback>) {
unsafe { (*self.context).registered_callback = Some(registered_callback) }; unsafe { (*self.context).registered_callback = Some(registered_callback) };
} }
/// Sets the optional user context to pass through to the callback. This is useful if you need fn set_context(&mut self, context: Box<dyn Any>) {
/// to share state between pre and post-callback. The context type must implement `Any`.
pub fn set_context(&mut self, context: Box<dyn Any>) {
unsafe { (*self.context).user_context = Some(Arc::from(context)) }; unsafe { (*self.context).user_context = Some(Arc::from(context)) };
} }
/// Registers and start's the service. Returns an `EventLoop` which can be called to keep fn register(&mut self) -> Result<EventLoop> {
/// the service alive.
pub fn register(&mut self) -> Result<EventLoop> {
debug!("Registering service: {:?}", self); debug!("Registering service: {:?}", self);
let txt_len = self let txt_len = self

View File

@ -1 +1,3 @@
pub use crate::browser::TMdnsBrowser;
pub use crate::service::TMdnsService;
pub use crate::txt_record::TTxtRecord; pub use crate::txt_record::TTxtRecord;

45
zeroconf/src/service.rs Normal file
View File

@ -0,0 +1,45 @@
use crate::{EventLoop, NetworkInterface, Result, ServiceRegisteredCallback, TxtRecord};
use std::any::Any;
pub trait TMdnsService {
/// Creates a new `MdnsService` with the specified `kind` (e.g. `_http._tcp`) and `port`.
fn new(kind: &str, port: u16) -> Self;
/// Sets the name to register this service under.
fn set_name(&mut self, name: &str);
/// Sets the network interface to bind this service to.
///
/// Most applications will want to use the default value `NetworkInterface::Unspec` to bind to
/// all available interfaces.
fn set_network_interface(&mut self, interface: NetworkInterface);
/// Sets the domain on which to advertise the service.
///
/// Most applications will want to use the default value of `ptr::null()` to register to the
/// default domain.
fn set_domain(&mut self, _domain: &str);
/// Sets the SRV target host name.
///
/// Most applications will want to use the default value of `ptr::null()` to use the machine's
/// default host name.
fn set_host(&mut self, _host: &str);
/// Sets the optional `TxtRecord` to register this service with.
fn set_txt_record(&mut self, txt_record: TxtRecord);
/// Sets the [`ServiceRegisteredCallback`] that is invoked when the service has been
/// registered.
///
/// [`ServiceRegisteredCallback`]: ../type.ServiceRegisteredCallback.html
fn set_registered_callback(&mut self, registered_callback: Box<ServiceRegisteredCallback>);
/// Sets the optional user context to pass through to the callback. This is useful if you need
/// to share state between pre and post-callback. The context type must implement `Any`.
fn set_context(&mut self, context: Box<dyn Any>);
/// Registers and start's the service. Returns an `EventLoop` which can be called to keep
/// the service alive.
fn register(&mut self) -> Result<EventLoop>;
}

View File

@ -1,3 +1,4 @@
use crate::prelude::*;
use crate::{MdnsBrowser, MdnsService}; use crate::{MdnsBrowser, MdnsService};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::time::Duration; use std::time::Duration;

View File

@ -59,8 +59,6 @@ fn iter_success() {
fn keys_success() { fn keys_success() {
super::setup(); super::setup();
debug!("keys_success()");
let mut record = TxtRecord::new(); let mut record = TxtRecord::new();
record.insert("foo", "bar").unwrap(); record.insert("foo", "bar").unwrap();
record.insert("baz", "qux").unwrap(); record.insert("baz", "qux").unwrap();
@ -76,8 +74,6 @@ fn keys_success() {
fn values_success() { fn values_success() {
super::setup(); super::setup();
debug!("values_success()");
let mut record = TxtRecord::new(); let mut record = TxtRecord::new();
record.insert("foo", "bar").unwrap(); record.insert("foo", "bar").unwrap();
record.insert("baz", "qux").unwrap(); record.insert("baz", "qux").unwrap();

View File

@ -80,7 +80,7 @@ impl PartialEq for TxtRecord {
} }
} }
// impl Eq for TxtRecord {} impl Eq for TxtRecord {}
impl Default for TxtRecord { impl Default for TxtRecord {
fn default() -> Self { fn default() -> Self {