diff --git a/examples/browser/src/main.rs b/examples/browser/src/main.rs index 0797c83..1c1c981 100644 --- a/examples/browser/src/main.rs +++ b/examples/browser/src/main.rs @@ -51,7 +51,7 @@ fn main() -> zeroconf::Result<()> { loop { // calling `poll()` will keep this browser alive - event_loop.poll(Duration::from_secs(0))?; + event_loop.poll(Duration::from_secs(1))?; } } @@ -59,10 +59,7 @@ fn on_service_discovered( result: zeroconf::Result, _context: Option>, ) { - info!( - "Service discovered: {:?}", - result.expect("service discovery failed") - ); + info!("Service discovery event: {:?}", result); // ... } diff --git a/zeroconf/src/avahi/avahi_util.rs b/zeroconf/src/avahi/avahi_util.rs index a683cd4..925d193 100644 --- a/zeroconf/src/avahi/avahi_util.rs +++ b/zeroconf/src/avahi/avahi_util.rs @@ -1,6 +1,6 @@ //! Utilities related to Avahi -use crate::ffi::c_str; +use crate::{ffi::c_str, Error}; use avahi_sys::{ avahi_address_snprint, avahi_alternative_service_name, avahi_strerror, AvahiAddress, AvahiClient, @@ -47,8 +47,14 @@ pub unsafe fn get_error<'a>(code: i32) -> &'a str { /// /// # Safety /// This function is unsafe because of internal Avahi calls. -pub unsafe fn get_last_error<'a>(client: *mut AvahiClient) -> &'a str { - get_error(avahi_sys::avahi_client_errno(client)) +pub unsafe fn get_last_error(client: *mut AvahiClient) -> Error { + let code = avahi_sys::avahi_client_errno(client); + let message = get_error(code); + + Error::MdnsSystemError { + code, + message: message.into(), + } } /// Converts the specified [`NetworkInterface`] to the Avahi expected value. @@ -77,7 +83,10 @@ pub unsafe fn sys_exec i32>(func: F, message: &str) -> Result<()> let err = func(); if err < 0 { - Err(format!("{}: `{}`", message, get_error(err)).into()) + Err(Error::MdnsSystemError { + code: err, + message: format!("{}: (code: {}, message:{:?})", message, err, get_error(err)).into(), + }) } else { Ok(()) } @@ -139,7 +148,10 @@ mod tests { fn sys_exec_returns_error_for_failure() { assert_eq!( unsafe { sys_exec(|| avahi_sys::AVAHI_ERR_FAILURE, "uh oh spaghetti-o") }, - Err("uh oh spaghetti-o: `Operation failed`".into()) + Err(Error::MdnsSystemError { + code: avahi_sys::AVAHI_ERR_FAILURE, + message: "uh oh spaghetti-o: (code: -1, message:\"Operation failed\")".into(), + }) ); } diff --git a/zeroconf/src/avahi/browser.rs b/zeroconf/src/avahi/browser.rs index f041178..2904db4 100644 --- a/zeroconf/src/avahi/browser.rs +++ b/zeroconf/src/avahi/browser.rs @@ -12,7 +12,7 @@ use super::{ }; use crate::ffi::{c_str, AsRaw, FromRaw}; use crate::prelude::*; -use crate::Result; +use crate::{Error, Result}; use crate::{ EventLoop, NetworkInterface, ServiceDiscoveredCallback, ServiceDiscovery, ServiceType, TxtRecord, @@ -74,12 +74,16 @@ impl TMdnsBrowser for AvahiMdnsBrowser { fn browse_services(&mut self) -> Result { debug!("Browsing services: {:?}", self); - self.poll = Some(Arc::new(unsafe { ManagedAvahiSimplePoll::new() }?)); + self.poll = Some(Arc::new( + unsafe { ManagedAvahiSimplePoll::new() }.ok_or_else(|| { + Error::BrowserError("could not initialize AvahiSimplePoll".into()) + })?, + )); let poll = self .poll .as_ref() - .ok_or("could not get poll as ref")? + .ok_or(Error::BrowserError("could not get poll as ref".into()))? .clone(); let client_params = ManagedAvahiClientParams::builder() @@ -87,7 +91,8 @@ impl TMdnsBrowser for AvahiMdnsBrowser { .flags(AvahiClientFlags(0)) .callback(Some(client_callback)) .userdata(self.context.as_raw()) - .build()?; + .build() + .map_err(Error::BrowserError)?; self.client = Some(Arc::new(unsafe { ManagedAvahiClient::new(client_params) }?)); @@ -102,7 +107,7 @@ impl TMdnsBrowser for AvahiMdnsBrowser { Ok(EventLoop::new( self.poll .as_ref() - .ok_or("could not get poll as ref")? + .ok_or(Error::BrowserError("could not get poll as ref".into()))? .clone(), )) } @@ -171,13 +176,11 @@ unsafe fn create_browser(context: &mut AvahiBrowserContext) -> Result<()> { .flags(0) .callback(Some(browse_callback)) .userdata(context.as_raw()) - .client(Arc::clone( - context - .client - .as_ref() - .ok_or("could not get client as ref")?, - )) - .build()?, + .client(Arc::clone(context.client.as_ref().ok_or( + Error::BrowserError("could not get client as ref".into()), + )?)) + .build() + .map_err(Error::BrowserError)?, )?); Ok(()) @@ -203,7 +206,7 @@ unsafe extern "C" fn browse_callback( } } avahi_sys::AvahiBrowserEvent_AVAHI_BROWSER_FAILURE => { - context.invoke_callback(Err("browser failure".into())) + context.invoke_callback(Err(Error::BrowserError("browser failure".into()))) } _ => {} }; @@ -222,7 +225,7 @@ unsafe fn handle_browser_new( let client = context .client .as_ref() - .ok_or("expected initialized client")?; + .ok_or(Error::BrowserError("expected initialized client".into()))?; context.resolvers.insert(ManagedAvahiServiceResolver::new( ManagedAvahiServiceResolverParams::builder() @@ -236,7 +239,8 @@ unsafe fn handle_browser_new( .flags(0) .callback(Some(resolve_callback)) .userdata(raw_context) - .build()?, + .build() + .map_err(Error::BrowserError)?, )?); Ok(()) @@ -265,11 +269,13 @@ unsafe extern "C" fn resolve_callback( match event { avahi_sys::AvahiResolverEvent_AVAHI_RESOLVER_FAILURE => { - context.invoke_callback(Err(format!( - "failed to resolve service `{}` of type `{}` in domain `{}`", - name, kind, domain - ) - .into())); + context.invoke_callback(Err(Error::BrowserError( + format!( + "failed to resolve service `{}` of type `{}` in domain `{}`", + name, kind, domain + ) + .into(), + ))); } avahi_sys::AvahiResolverEvent_AVAHI_RESOLVER_FOUND => { let result = handle_resolver_found( @@ -320,7 +326,8 @@ unsafe fn handle_resolver_found( .address(address) .port(port) .txt(txt) - .build()?; + .build() + .map_err(Error::BrowserError)?; debug!("Service resolved: {:?}", result); diff --git a/zeroconf/src/avahi/client.rs b/zeroconf/src/avahi/client.rs index ecc6075..6cec903 100644 --- a/zeroconf/src/avahi/client.rs +++ b/zeroconf/src/avahi/client.rs @@ -2,9 +2,10 @@ use std::sync::Arc; -use super::{avahi_util, poll::ManagedAvahiSimplePoll}; +use super::avahi_util; +use super::poll::ManagedAvahiSimplePoll; use crate::ffi::c_str; -use crate::Result; +use crate::{Error, Result}; use avahi_sys::{ avahi_client_free, avahi_client_get_host_name, avahi_client_new, avahi_simple_poll_get, AvahiClient, AvahiClientCallback, AvahiClientFlags, @@ -46,17 +47,13 @@ impl ManagedAvahiClient { ); if inner.is_null() { - return Err("could not initialize AvahiClient".into()); + return Err(Error::MdnsSystemError { + code: err, + message: avahi_util::get_error(err).into(), + }); } - match err { - 0 => Ok(Self { inner, _poll: poll }), - _ => Err(format!( - "could not initialize AvahiClient: {}", - avahi_util::get_error(err) - ) - .into()), - } + Ok(Self { inner, _poll: poll }) } /// Delegate function for [`avahi_client_get_host_name()`]. @@ -99,6 +96,6 @@ pub(super) unsafe fn get_host_name<'a>(client: *mut AvahiClient) -> Result<&'a s if !host_name.is_null() { Ok(c_str::raw_to_str(host_name)) } else { - Err("could not get host name from AvahiClient".into()) + Err(avahi_util::get_last_error(client)) } } diff --git a/zeroconf/src/avahi/entry_group.rs b/zeroconf/src/avahi/entry_group.rs index 9babd93..bd5ea3a 100644 --- a/zeroconf/src/avahi/entry_group.rs +++ b/zeroconf/src/avahi/entry_group.rs @@ -7,10 +7,10 @@ use crate::avahi::avahi_util; use crate::ffi::UnwrapMutOrNull; use crate::Result; use avahi_sys::{ - avahi_client_errno, avahi_entry_group_add_service_strlst, - avahi_entry_group_add_service_subtype, avahi_entry_group_commit, avahi_entry_group_free, - avahi_entry_group_is_empty, avahi_entry_group_new, avahi_entry_group_reset, AvahiClient, - AvahiEntryGroup, AvahiEntryGroupCallback, AvahiIfIndex, AvahiProtocol, AvahiPublishFlags, + avahi_entry_group_add_service_strlst, avahi_entry_group_add_service_subtype, + avahi_entry_group_commit, avahi_entry_group_free, avahi_entry_group_is_empty, + avahi_entry_group_new, avahi_entry_group_reset, AvahiClient, AvahiEntryGroup, + AvahiEntryGroupCallback, AvahiIfIndex, AvahiProtocol, AvahiPublishFlags, }; use libc::{c_char, c_void}; @@ -40,8 +40,7 @@ impl ManagedAvahiEntryGroup { let inner = avahi_entry_group_new(client.inner, callback, userdata); if inner.is_null() { - let err = avahi_util::get_error(avahi_client_errno(client.inner)); - Err(format!("could not initialize AvahiEntryGroup: {}", err).into()) + Err(avahi_util::get_last_error(client.inner)) } else { Ok(Self { inner, diff --git a/zeroconf/src/avahi/poll.rs b/zeroconf/src/avahi/poll.rs index 6a5096b..bbdb8df 100644 --- a/zeroconf/src/avahi/poll.rs +++ b/zeroconf/src/avahi/poll.rs @@ -21,12 +21,12 @@ impl ManagedAvahiSimplePoll { /// /// # Safety /// This function is unsafe because of the raw pointer dereference. - pub unsafe fn new() -> Result { + pub unsafe fn new() -> Option { let poll = avahi_simple_poll_new(); if poll.is_null() { - Err("could not initialize AvahiSimplePoll".into()) + None } else { - Ok(Self(poll)) + Some(Self(poll)) } } @@ -58,12 +58,14 @@ impl ManagedAvahiSimplePoll { // Returns -1 on error, 0 on success and 1 if a quit request has been scheduled match avahi_simple_poll_iterate(self.0, sleep_time) { 0 | 1 => Ok(()), - -1 => Err(Error::from( - "avahi_simple_poll_iterate(..) threw an error result", - )), - _ => Err(Error::from( - "avahi_simple_poll_iterate(..) returned an unknown result", - )), + -1 => Err(Error::MdnsSystemError { + code: -1, // Translates to AVAHI_ERR_FAILURE with description "Generic error code". + message: "avahi_simple_poll_iterate(..) threw an error result".into(), + }), + err => Err(Error::MdnsSystemError { + code: err, + message: "avahi_simple_poll_iterate(..) returned an unknown result".into(), + }), } } diff --git a/zeroconf/src/avahi/raw_browser.rs b/zeroconf/src/avahi/raw_browser.rs index 8124539..2e20a59 100644 --- a/zeroconf/src/avahi/raw_browser.rs +++ b/zeroconf/src/avahi/raw_browser.rs @@ -2,7 +2,7 @@ use std::sync::Arc; -use crate::Result; +use crate::{avahi::avahi_util, Result}; use avahi_sys::{ avahi_service_browser_free, avahi_service_browser_get_client, avahi_service_browser_new, AvahiClient, AvahiIfIndex, AvahiLookupFlags, AvahiProtocol, AvahiServiceBrowser, @@ -52,7 +52,7 @@ impl ManagedAvahiServiceBrowser { ); if inner.is_null() { - Err("could not initialize Avahi service browser".into()) + Err(avahi_util::get_last_error(client.inner)) } else { Ok(Self { inner, diff --git a/zeroconf/src/avahi/resolver.rs b/zeroconf/src/avahi/resolver.rs index 3ff11a3..fda9b33 100644 --- a/zeroconf/src/avahi/resolver.rs +++ b/zeroconf/src/avahi/resolver.rs @@ -1,6 +1,6 @@ //! Rust friendly `AvahiServiceResolver` wrappers/helpers -use crate::Result; +use crate::{avahi::avahi_util, Result}; use avahi_sys::{ avahi_service_resolver_free, avahi_service_resolver_new, AvahiIfIndex, AvahiLookupFlags, AvahiProtocol, AvahiServiceResolver, AvahiServiceResolverCallback, @@ -55,7 +55,7 @@ impl ManagedAvahiServiceResolver { ); if inner.is_null() { - Err("could not initialize AvahiServiceResolver".into()) + Err(avahi_util::get_last_error(client.inner)) } else { Ok(Self { inner, diff --git a/zeroconf/src/avahi/service.rs b/zeroconf/src/avahi/service.rs index 5e068bc..3e11124 100644 --- a/zeroconf/src/avahi/service.rs +++ b/zeroconf/src/avahi/service.rs @@ -9,7 +9,7 @@ use super::poll::ManagedAvahiSimplePoll; use crate::ffi::{c_str, AsRaw, FromRaw, UnwrapOrNull}; use crate::prelude::*; use crate::{ - EventLoop, NetworkInterface, Result, ServiceRegisteredCallback, ServiceRegistration, + Error, EventLoop, NetworkInterface, Result, ServiceRegisteredCallback, ServiceRegistration, ServiceType, TxtRecord, }; use avahi_sys::{ @@ -110,12 +110,16 @@ impl TMdnsService for AvahiMdnsService { fn register(&mut self) -> Result { debug!("Registering service: {:?}", self); - self.poll = Some(Arc::new(unsafe { ManagedAvahiSimplePoll::new() }?)); + self.poll = Some(Arc::new( + unsafe { ManagedAvahiSimplePoll::new() }.ok_or_else(|| { + Error::ServiceError("could not initialize AvahiSimplePoll".into()) + })?, + )); let poll = self .poll .as_ref() - .ok_or("could not get poll as ref")? + .ok_or(Error::ServiceError("could not get poll as ref".into()))? .clone(); let client_params = ManagedAvahiClientParams::builder() @@ -123,7 +127,8 @@ impl TMdnsService for AvahiMdnsService { .flags(AvahiClientFlags(0)) .callback(Some(client_callback)) .userdata(self.context.as_raw()) - .build()?; + .build() + .map_err(Error::ServiceError)?; self.client = Some(Arc::new(unsafe { ManagedAvahiClient::new(client_params) }?)); @@ -138,7 +143,7 @@ impl TMdnsService for AvahiMdnsService { Ok(EventLoop::new( self.poll .as_ref() - .ok_or("could not get poll as ref")? + .ok_or(Error::ServiceError("could not get poll as ref".into()))? .clone(), )) } @@ -220,7 +225,7 @@ unsafe fn create_service(context: &mut AvahiServiceContext) -> Result<()> { let host_name = context .client .as_ref() - .ok_or("expected initialized client")? + .ok_or(Error::ServiceError("expected initialized client".into()))? .host_name()?; context.name = Some(c_string!(host_name.to_string())); @@ -231,22 +236,20 @@ unsafe fn create_service(context: &mut AvahiServiceContext) -> Result<()> { context.group = Some(ManagedAvahiEntryGroup::new( ManagedAvahiEntryGroupParams::builder() - .client(Arc::clone( - context - .client - .as_ref() - .ok_or("could not get client as ref")?, - )) + .client(Arc::clone(context.client.as_ref().ok_or( + Error::ServiceError("could not get client as ref".into()), + )?)) .callback(Some(entry_group_callback)) .userdata(context.as_raw()) - .build()?, + .build() + .map_err(Error::ServiceError)?, )?); } let group = context .group .as_mut() - .ok_or("could not borrow group as mut")?; + .ok_or(Error::ServiceError("could not borrow group as mut".into()))?; if !group.is_empty() { return Ok(()); @@ -255,7 +258,7 @@ unsafe fn create_service(context: &mut AvahiServiceContext) -> Result<()> { let name = context .name .as_ref() - .ok_or("could not get name as ref")? + .ok_or(Error::ServiceError("could not get name as ref".into()))? .clone(); add_services(context, &name) @@ -267,7 +270,7 @@ unsafe fn add_services(context: &mut AvahiServiceContext, name: &CStr) -> Result let group = context .group .as_mut() - .ok_or("could not borrow group as mut")?; + .ok_or(Error::ServiceError("could not borrow group as mut".into()))?; let params = AddServiceParams::builder() .interface(context.interface_index) @@ -279,7 +282,8 @@ unsafe fn add_services(context: &mut AvahiServiceContext, name: &CStr) -> Result .host(context.host.as_ref().map(|h| h.as_ptr()).unwrap_or_null()) .port(context.port) .txt(context.txt_record.as_ref().map(|t| t.inner())) - .build()?; + .build() + .map_err(Error::ServiceError)?; group.add_service(params)?; @@ -294,7 +298,8 @@ unsafe fn add_services(context: &mut AvahiServiceContext, name: &CStr) -> Result .kind(context.kind.as_ptr()) .domain(context.domain.as_ref().map(|d| d.as_ptr()).unwrap_or_null()) .subtype(sub_type.as_ptr()) - .build()?; + .build() + .map_err(Error::ServiceError)?; group.add_service_subtype(params)?; } @@ -348,7 +353,7 @@ unsafe fn handle_group_established(context: &AvahiServiceContext) -> Result Result DNSServiceErrorType>(func: F, message: &str) -> R if err < 0 { Err(Error::MdnsSystemError { code: err, - message: message.into(), + message: format!("{} (code: {})", message, err).into(), }) } else { Ok(()) @@ -136,9 +136,11 @@ mod tests { #[test] fn sys_exec_returns_error() { - let expected = Error::InvalidServiceType("uh oh spaghetti-o (code: -42)".into()); let result = sys_exec(|| -42, "uh oh spaghetti-o"); - assert_eq!(result, Err(expected)); + assert_eq!(result, Err(Error::MdnsSystemError { + code: -42, + message: "uh oh spaghetti-o (code: -42)".into(), + })); } #[test]