Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/windy1/zeroconf-rs
Browse files Browse the repository at this point in the history
  • Loading branch information
Walker Crouse committed Jan 29, 2024
2 parents 07d25fb + 0665ca7 commit 52cb1dc
Show file tree
Hide file tree
Showing 17 changed files with 284 additions and 94 deletions.
124 changes: 100 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,101 +14,174 @@ $ sudo apt install xorg-dev libxcb-shape0-dev libxcb-xfixes0-dev clang avahi-dae

On Windows:

Bonjour must be installed. It comes bundled with [iTunes](https://support.apple.com/en-us/HT210384) or [Bonjour Print Services](https://support.apple.com/kb/dl999). Further redistribution & bundling details are available on the [Apple Developer Site](https://developer.apple.com/licensing-trademarks/bonjour/).
Bonjour must be installed. It comes bundled with [iTunes][] or [Bonjour Print Services][]. Further redistribution &
bundling details are available on the [Apple Developer Site][].

## TODO
## Examples

* You tell me...

# Examples

## Register a service
### Register a service

When registering a service, you may optionally pass a "context" to pass state through the
callback. The only requirement is that this context implements the [`Any`] trait, which most
types will automatically. See `MdnsService` for more information about contexts.

```rust
#[macro_use]
extern crate log;

use clap::Parser;

use std::any::Any;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use zeroconf::prelude::*;
use zeroconf::{MdnsService, ServiceRegistration, ServiceType, TxtRecord};

#[derive(Parser, Debug)]
#[command(author, version, about)]
struct Args {
/// Name of the service type to register
#[clap(short, long, default_value = "http")]
name: String,

/// Protocol of the service type to register
#[clap(short, long, default_value = "tcp")]
protocol: String,

/// Sub-types of the service type to register
#[clap(short, long)]
sub_types: Vec<String>,
}

#[derive(Default, Debug)]
pub struct Context {
service_name: String,
}

fn main() {
let mut service = MdnsService::new(ServiceType::new("http", "tcp").unwrap(), 8080);
fn main() -> zeroconf::Result<()> {
env_logger::init();

let Args {
name,
protocol,
sub_types,
} = Args::parse();

let sub_types = sub_types.iter().map(|s| s.as_str()).collect::<Vec<_>>();
let service_type = ServiceType::with_sub_types(&name, &protocol, sub_types)?;
let mut service = MdnsService::new(service_type, 8080);
let mut txt_record = TxtRecord::new();
let context: Arc<Mutex<Context>> = Arc::default();

txt_record.insert("foo", "bar").unwrap();
txt_record.insert("foo", "bar")?;

service.set_name("zeroconf_example_service");
service.set_registered_callback(Box::new(on_service_registered));
service.set_context(Box::new(context));
service.set_txt_record(txt_record);

let event_loop = service.register().unwrap();
let event_loop = service.register()?;

loop {
// calling `poll()` will keep this service alive
event_loop.poll(Duration::from_secs(0)).unwrap();
event_loop.poll(Duration::from_secs(0))?;
}
}

fn on_service_registered(
result: zeroconf::Result<ServiceRegistration>,
context: Option<Arc<dyn Any>>,
) {
let service = result.unwrap();
let service = result.expect("failed to register service");

println!("Service registered: {:?}", service);
info!("Service registered: {:?}", service);

let context = context
.as_ref()
.unwrap()
.expect("could not get context")
.downcast_ref::<Arc<Mutex<Context>>>()
.unwrap()
.expect("error down-casting context")
.clone();

context.lock().unwrap().service_name = service.name().clone();
context
.lock()
.expect("failed to obtain context lock")
.service_name = service.name().clone();

println!("Context: {:?}", context);
info!("Context: {:?}", context);

// ...
}
```

## Browsing services
### Browsing services

```rust
#[macro_use]
extern crate log;

use clap::Parser;

use std::any::Any;
use std::sync::Arc;
use std::time::Duration;
use zeroconf::prelude::*;
use zeroconf::{MdnsBrowser, ServiceDiscovery, ServiceType};

fn main() {
let mut browser = MdnsBrowser::new(ServiceType::new("http", "tcp").unwrap());
/// Example of a simple mDNS browser
#[derive(Parser, Debug)]
#[command(author, version, about)]
struct Args {
/// Name of the service type to browse
#[clap(short, long, default_value = "http")]
name: String,

/// Protocol of the service type to browse
#[clap(short, long, default_value = "tcp")]
protocol: String,

/// Sub-type of the service type to browse
#[clap(short, long)]
sub_type: Option<String>,
}

fn main() -> zeroconf::Result<()> {
env_logger::init();

let Args {
name,
protocol,
sub_type,
} = Args::parse();

let sub_types: Vec<&str> = match sub_type.as_ref() {
Some(sub_type) => vec![sub_type],
None => vec![],
};

let service_type =
ServiceType::with_sub_types(&name, &protocol, sub_types).expect("invalid service type");

let mut browser = MdnsBrowser::new(service_type);

browser.set_service_discovered_callback(Box::new(on_service_discovered));

let event_loop = browser.browse_services().unwrap();
let event_loop = browser.browse_services()?;

loop {
// calling `poll()` will keep this browser alive
event_loop.poll(Duration::from_secs(0)).unwrap();
event_loop.poll(Duration::from_secs(0))?;
}
}

fn on_service_discovered(
result: zeroconf::Result<ServiceDiscovery>,
_context: Option<Arc<dyn Any>>,
) {
println!("Service discovered: {:?}", result.unwrap());
info!(
"Service discovered: {:?}",
result.expect("service discovery failed")
);

// ...
}
Expand All @@ -125,3 +198,6 @@ fn on_service_discovered(
[`Any`]: https://doc.rust-lang.org/std/any/trait.Any.html
[Avahi docs]: https://avahi.org/doxygen/html/
[Bonjour docs]: https://developer.apple.com/documentation/dnssd/dns_service_discovery_c
[iTunes]: https://support.apple.com/en-us/HT210384
[Bonjour Print Services]: https://developer.apple.com/licensing-trademarks/bonjour/
[Apple Developer Site]: https://developer.apple.com/licensing-trademarks/bonjour/
11 changes: 7 additions & 4 deletions examples/browser/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ struct Args {
sub_type: Option<String>,
}

fn main() {
fn main() -> zeroconf::Result<()> {
env_logger::init();

let Args {
Expand All @@ -47,19 +47,22 @@ fn main() {

browser.set_service_discovered_callback(Box::new(on_service_discovered));

let event_loop = browser.browse_services().unwrap();
let event_loop = browser.browse_services()?;

loop {
// calling `poll()` will keep this browser alive
event_loop.poll(Duration::from_secs(0)).unwrap();
event_loop.poll(Duration::from_secs(0))?;
}
}

fn on_service_discovered(
result: zeroconf::Result<ServiceDiscovery>,
_context: Option<Arc<dyn Any>>,
) {
info!("Service discovered: {:?}", result.unwrap());
info!(
"Service discovered: {:?}",
result.expect("service discovery failed")
);

// ...
}
21 changes: 12 additions & 9 deletions examples/service/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub struct Context {
service_name: String,
}

fn main() {
fn main() -> zeroconf::Result<()> {
env_logger::init();

let Args {
Expand All @@ -40,42 +40,45 @@ fn main() {
} = Args::parse();

let sub_types = sub_types.iter().map(|s| s.as_str()).collect::<Vec<_>>();
let service_type = ServiceType::with_sub_types(&name, &protocol, sub_types).unwrap();
let service_type = ServiceType::with_sub_types(&name, &protocol, sub_types)?;
let mut service = MdnsService::new(service_type, 8080);
let mut txt_record = TxtRecord::new();
let context: Arc<Mutex<Context>> = Arc::default();

txt_record.insert("foo", "bar").unwrap();
txt_record.insert("foo", "bar")?;

service.set_name("zeroconf_example_service");
service.set_registered_callback(Box::new(on_service_registered));
service.set_context(Box::new(context));
service.set_txt_record(txt_record);

let event_loop = service.register().unwrap();
let event_loop = service.register()?;

loop {
// calling `poll()` will keep this service alive
event_loop.poll(Duration::from_secs(0)).unwrap();
event_loop.poll(Duration::from_secs(0))?;
}
}

fn on_service_registered(
result: zeroconf::Result<ServiceRegistration>,
context: Option<Arc<dyn Any>>,
) {
let service = result.unwrap();
let service = result.expect("failed to register service");

info!("Service registered: {:?}", service);

let context = context
.as_ref()
.unwrap()
.expect("could not get context")
.downcast_ref::<Arc<Mutex<Context>>>()
.unwrap()
.expect("error down-casting context")
.clone();

context.lock().unwrap().service_name = service.name().clone();
context
.lock()
.expect("failed to obtain context lock")
.service_name = service.name().clone();

info!("Context: {:?}", context);

Expand Down
13 changes: 8 additions & 5 deletions zeroconf-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use syn::{self, DeriveInput, Ident};

#[proc_macro_derive(FromRaw)]
pub fn from_raw_macro_derive(input: TokenStream) -> TokenStream {
impl_from_raw(&syn::parse(input).unwrap())
impl_from_raw(&syn::parse(input).expect("could not parse input"))
}

fn impl_from_raw(ast: &DeriveInput) -> TokenStream {
Expand All @@ -22,7 +22,7 @@ fn impl_from_raw(ast: &DeriveInput) -> TokenStream {

#[proc_macro_derive(CloneRaw)]
pub fn clone_raw_macro_derive(input: TokenStream) -> TokenStream {
impl_clone_raw(&syn::parse(input).unwrap())
impl_clone_raw(&syn::parse(input).expect("could not parse input"))
}

fn impl_clone_raw(ast: &DeriveInput) -> TokenStream {
Expand All @@ -38,7 +38,7 @@ fn impl_clone_raw(ast: &DeriveInput) -> TokenStream {

#[proc_macro_derive(AsRaw)]
pub fn as_raw_macro_derive(input: TokenStream) -> TokenStream {
impl_as_raw(&syn::parse(input).unwrap())
impl_as_raw(&syn::parse(input).expect("could not parse input"))
}

fn impl_as_raw(ast: &DeriveInput) -> TokenStream {
Expand All @@ -54,12 +54,15 @@ fn impl_as_raw(ast: &DeriveInput) -> TokenStream {

#[proc_macro_derive(BuilderDelegate)]
pub fn builder_delegate_macro_derive(input: TokenStream) -> TokenStream {
impl_builder_delegate(&syn::parse(input).unwrap())
impl_builder_delegate(&syn::parse(input).expect("could not parse input"))
}

fn impl_builder_delegate(ast: &DeriveInput) -> TokenStream {
let name = &ast.ident;
let builder: Ident = syn::parse_str(&format!("{}Builder", name)).unwrap();

let builder: Ident =
syn::parse_str(&format!("{}Builder", name)).expect("could not parse builder name");

let generics = &ast.generics;

let gen = quote! {
Expand Down
3 changes: 2 additions & 1 deletion zeroconf/src/avahi/avahi_util.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Utilities related to Avahi

use crate::ffi::c_str;
use avahi_sys::{
avahi_address_snprint, avahi_alternative_service_name, avahi_strerror, AvahiAddress,
AvahiClient,
Expand Down Expand Up @@ -27,7 +28,7 @@ pub unsafe fn avahi_address_to_string(addr: *const AvahiAddress) -> String {
addr,
);

String::from(addr_str.to_str().unwrap())
String::from(c_str::to_str(&addr_str))
.trim_matches(char::from(0))
.to_string()
}
Expand Down
Loading

0 comments on commit 52cb1dc

Please sign in to comment.