diff --git a/zbus/src/address/address_list.rs b/zbus/src/address/address_list.rs index 8d7d0f8f0..16659c1f2 100644 --- a/zbus/src/address/address_list.rs +++ b/zbus/src/address/address_list.rs @@ -1,6 +1,6 @@ use std::{borrow::Cow, fmt}; -use super::{Address, Error, Result, ToAddresses}; +use super::{Address, Error, OwnedAddress, Result, ToAddresses, ToOwnedAddresses}; /// A bus address list. /// @@ -79,3 +79,47 @@ impl fmt::Display for AddressList<'_> { write!(f, "{}", self.addr) } } + +/// An iterator of D-Bus addresses. +pub struct OwnedAddressListIter<'a> { + data: &'a str, + next_index: usize, +} + +impl<'a> ToOwnedAddresses<'a> for AddressList<'a> { + type Iter = OwnedAddressListIter<'a>; + + /// Get an iterator over the D-Bus addresses. + fn to_owned_addresses(&'a self) -> Self::Iter { + OwnedAddressListIter::new(self) + } +} + +impl<'a> OwnedAddressListIter<'a> { + fn new(list: &'a AddressList<'_>) -> Self { + Self { + data: list.addr.as_ref(), + next_index: 0, + } + } +} + +impl<'a> Iterator for OwnedAddressListIter<'a> { + type Item = Result; + + fn next(&mut self) -> Option { + if self.next_index >= self.data.len() { + return None; + } + + let mut addr = &self.data[self.next_index..]; + if let Some(end) = addr.find(';') { + addr = &addr[..end]; + self.next_index += end + 1; + } else { + self.next_index = self.data.len(); + } + + Some(OwnedAddress::try_from(addr)) + } +} diff --git a/zbus/src/address/guid.rs b/zbus/src/address/guid.rs new file mode 100644 index 000000000..b4b73dbdb --- /dev/null +++ b/zbus/src/address/guid.rs @@ -0,0 +1,45 @@ +use std::fmt; + +use super::{Error, Result}; + +/// Universally-unique IDs for D-Bus addresses. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Guid([u8; 16]); + +impl Guid { + /// Create a Guid from the given bytes. + pub fn new(bytes: [u8; 16]) -> Self { + Guid(bytes) + } + + /// Returns a byte slice of this Guid’s contents + pub fn as_bytes(&self) -> &[u8; 16] { + &self.0 + } +} + +impl fmt::Display for Guid { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", + self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], self.0[6], self.0[7], + self.0[8], self.0[9], self.0[10], self.0[11], self.0[12], self.0[13], self.0[14], self.0[15]) + } +} + +impl TryFrom<&str> for Guid { + type Error = Error; + + fn try_from(s: &str) -> Result { + if s.len() != 32 { + return Err(Error::InvalidValue("guid".into())); + } + + let mut bytes = [0u8; 16]; + for (i, chunk) in s.as_bytes().chunks(2).enumerate() { + bytes[i] = u8::from_str_radix(std::str::from_utf8(chunk).unwrap(), 16) + .map_err(|_| Error::InvalidValue("guid".into()))?; + } + + Ok(Guid(bytes)) + } +} diff --git a/zbus/src/address/mod.rs b/zbus/src/address/mod.rs index c50eb056b..a90f20629 100644 --- a/zbus/src/address/mod.rs +++ b/zbus/src/address/mod.rs @@ -33,6 +33,10 @@ pub use address_list::{AddressList, AddressListIter}; mod percent; pub use percent::*; +use transport::TransportImpl; + +pub mod guid; +use guid::Guid; #[cfg(test)] mod tests; @@ -119,7 +123,10 @@ pub fn system() -> Result> { } } -/// A bus address. +/// A parsed bus address. +/// +/// The fields of this structure are references to the source. Using an [`OwnedAddress`] may be more +/// convenient or if your context requires 'static lifetime. /// /// Example: /// ``` @@ -134,13 +141,17 @@ pub struct Address<'a> { impl<'a> Address<'a> { /// The connection GUID if any. - pub fn guid(&self) -> Option> { - self.get_string("guid").and_then(|res| res.ok()) + pub fn guid(&self) -> Result> { + if let Some(guid) = self.get_string("guid") { + Ok(Some(guid?.as_ref().try_into()?)) + } else { + Ok(None) + } } /// Transport connection details pub fn transport(&self) -> Result> { - self.try_into() + transport::Transport::for_address(self) } pub(super) fn key_val_iter(&'a self) -> KeyValIter<'a> { @@ -163,12 +174,14 @@ impl<'a> Address<'a> { fn validate(&self) -> Result<()> { self.transport()?; for (k, v) in self.key_val_iter() { - let v = match v { - Some(v) => decode_percents(v)?, - _ => Cow::from(b"" as &[_]), - }; - if k == "guid" { - validate_guid(v.as_ref())?; + match (k, v) { + ("guid", Some(v)) => { + guid::Guid::try_from(decode_percents_str(v)?.as_ref())?; + } + (_, Some(v)) => { + decode_percents(v)?; + } + _ => {} } } @@ -187,21 +200,6 @@ impl<'a> Address<'a> { } } -fn validate_guid(value: &[u8]) -> Result<()> { - if value.len() != 32 || value.iter().any(|&c| !c.is_ascii_hexdigit()) { - return Err(Error::InvalidValue("guid".into())); - } - - Ok(()) -} - -impl Address<'_> { - pub fn to_owned(&self) -> Address<'static> { - let addr = self.addr.to_string(); - Address { addr: addr.into() } - } -} - impl<'a> TryFrom for Address<'a> { type Error = Error; @@ -220,14 +218,73 @@ impl<'a> TryFrom<&'a str> for Address<'a> { impl fmt::Display for Address<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let kv = KeyValFmt::new().add("guid", self.guid()); + let kv = KeyValFmt::new().add("guid", self.guid().map_err(|_| fmt::Error)?); let t = self.transport().map_err(|_| fmt::Error)?; - let kv = t.key_val_fmt_add(kv); + let kv = t.fmt_key_val(kv); + write!(f, "{t}:{kv}")?; + Ok(()) + } +} + +/// An owned bus address. +/// +/// Example: +/// ``` +/// use zbus::address::OwnedAddress; +/// +/// let _: OwnedAddress = "unix:path=/tmp/dbus.sock".try_into().unwrap(); +/// ``` +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct OwnedAddress { + transport: transport::Transport<'static>, + guid: Option, +} + +impl OwnedAddress { + /// The connection GUID if any. + pub fn guid(&self) -> Option<&Guid> { + self.guid.as_ref() + } + + /// Transport connection details + pub fn transport(&self) -> &transport::Transport<'static> { + &self.transport + } + + fn new(addr: &str) -> Result { + let addr = Address { addr: addr.into() }; + let transport = addr.transport()?.into_owned(); + let guid = addr.guid()?; + Ok(Self { transport, guid }) + } +} + +impl fmt::Display for OwnedAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let kv = KeyValFmt::new().add("guid", self.guid.as_ref()); + let t = &self.transport; + let kv = t.fmt_key_val(kv); write!(f, "{t}:{kv}")?; Ok(()) } } +impl TryFrom<&str> for OwnedAddress { + type Error = Error; + + fn try_from(addr: &str) -> Result { + Self::new(addr) + } +} + +impl TryFrom for OwnedAddress { + type Error = Error; + + fn try_from(addr: String) -> Result { + Self::new(&addr) + } +} + pub(super) struct KeyValIter<'a> { data: &'a str, next_index: usize, @@ -265,10 +322,10 @@ impl<'a> Iterator for KeyValIter<'a> { } } -pub(crate) trait KeyValFmtAdd { - fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b>; -} - +// A structure for formatting key-value pairs. +// +// This struct allows for the dynamic collection and formatting of key-value pairs, +// where keys implement `fmt::Display` and values implement `Encodable`. pub(crate) struct KeyValFmt<'a> { fields: Vec<(Box, Box)>, } @@ -342,8 +399,23 @@ impl<'a> ToAddresses<'a> for String { impl<'a> ToAddresses<'a> for Vec>> { type Iter = std::iter::Cloned>>>; - /// Get an iterator over the D-Bus addresses. fn to_addresses(&'a self) -> Self::Iter { self.iter().cloned() } } + +/// A trait for objects which can be converted or resolved to one or more [`OwnedAddress`] values. +pub trait ToOwnedAddresses<'a> { + type Iter: Iterator>; + + /// Get an iterator over the D-Bus addresses. + fn to_owned_addresses(&'a self) -> Self::Iter; +} + +impl<'a> ToOwnedAddresses<'a> for str { + type Iter = std::iter::Once>; + + fn to_owned_addresses(&'a self) -> Self::Iter { + std::iter::once(self.try_into()) + } +} diff --git a/zbus/src/address/percent.rs b/zbus/src/address/percent.rs index ccd2c6310..a65c1891a 100644 --- a/zbus/src/address/percent.rs +++ b/zbus/src/address/percent.rs @@ -6,39 +6,6 @@ use std::{ use super::{Error, Result}; -// A trait for types that can be percent-encoded and written to a [`fmt::Formatter`]. -pub(crate) trait Encodable { - fn encode(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result; -} - -impl Encodable for T { - fn encode(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { - encode_percents(f, self.to_string().as_bytes()) - } -} - -pub(crate) struct EncData(pub T); - -impl> Encodable for EncData { - fn encode(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { - encode_percents(f, self.0.as_ref()) - } -} - -pub(crate) struct EncOsStr(pub T); - -impl Encodable for EncOsStr<&Cow<'_, OsStr>> { - fn encode(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { - encode_percents(f, self.0.to_string_lossy().as_bytes()) - } -} - -impl Encodable for EncOsStr<&OsStr> { - fn encode(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { - encode_percents(f, self.0.to_string_lossy().as_bytes()) - } -} - /// Percent-encode the value. pub fn encode_percents(f: &mut dyn fmt::Write, value: &[u8]) -> std::fmt::Result { for &byte in value { @@ -86,6 +53,39 @@ pub fn decode_percents(value: &str) -> Result> { Ok(Cow::Owned(decoded)) } +// A trait for types that can be percent-encoded and written to a [`fmt::Formatter`]. +pub(crate) trait Encodable { + fn encode(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result; +} + +impl Encodable for T { + fn encode(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { + encode_percents(f, self.to_string().as_bytes()) + } +} + +pub(crate) struct EncData(pub T); + +impl> Encodable for EncData { + fn encode(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { + encode_percents(f, self.0.as_ref()) + } +} + +pub(crate) struct EncOsStr(pub T); + +impl Encodable for EncOsStr<&Cow<'_, OsStr>> { + fn encode(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { + encode_percents(f, self.0.to_string_lossy().as_bytes()) + } +} + +impl Encodable for EncOsStr<&OsStr> { + fn encode(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { + encode_percents(f, self.0.to_string_lossy().as_bytes()) + } +} + fn is_allowed_char(c: char) -> bool { matches!(c, '-' | '0'..='9' | 'A'..='Z' | 'a'..='z' | '_' | '/' | '.' | '\\' | '*') } diff --git a/zbus/src/address/transport/autolaunch.rs b/zbus/src/address/transport/autolaunch.rs index 3d56a907f..8573968db 100644 --- a/zbus/src/address/transport/autolaunch.rs +++ b/zbus/src/address/transport/autolaunch.rs @@ -4,49 +4,12 @@ use std::{borrow::Cow, fmt}; #[cfg(target_os = "windows")] use super::percent::decode_percents_str; -use super::{Address, Error, KeyValFmt, KeyValFmtAdd, Result}; - -/// Scope of autolaunch (Windows only) -#[cfg(target_os = "windows")] -#[derive(Debug, PartialEq, Eq)] -#[non_exhaustive] -pub enum AutolaunchScope<'a> { - /// Limit session bus to dbus installation path. - InstallPath, - /// Limit session bus to the recent user. - User, - /// other values - specify dedicated session bus like "release", "debug" or other. - Other(Cow<'a, str>), -} - -#[cfg(target_os = "windows")] -impl fmt::Display for AutolaunchScope<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::InstallPath => write!(f, "*install-path"), - Self::User => write!(f, "*user"), - Self::Other(o) => write!(f, "{o}"), - } - } -} - -#[cfg(target_os = "windows")] -impl<'a> TryFrom> for AutolaunchScope<'a> { - type Error = Error; - - fn try_from(s: Cow<'a, str>) -> Result { - match s.as_ref() { - "*install-path" => Ok(Self::InstallPath), - "*user" => Ok(Self::User), - _ => Ok(Self::Other(s)), - } - } -} +use super::{Address, KeyValFmt, Result, TransportImpl}; /// `autolaunch:` D-Bus transport. /// /// -#[derive(Debug, PartialEq, Eq, Default)] +#[derive(Clone, Debug, PartialEq, Eq, Default)] pub struct Autolaunch<'a> { #[cfg(target_os = "windows")] scope: Option>, @@ -59,12 +22,19 @@ impl<'a> Autolaunch<'a> { pub fn scope(&self) -> Option<&AutolaunchScope<'a>> { self.scope.as_ref() } -} -impl<'a> TryFrom<&'a Address<'a>> for Autolaunch<'a> { - type Error = Error; + /// Convert into owned version, with 'static lifetime. + pub fn into_owned(self) -> Autolaunch<'static> { + Autolaunch { + #[cfg(target_os = "windows")] + scope: self.scope.map(|s| s.into_owned()), + phantom: PhantomData, + } + } +} - fn try_from(s: &'a Address<'a>) -> Result { +impl<'a> TransportImpl<'a> for Autolaunch<'a> { + fn for_address(s: &'a Address<'a>) -> Result { #[allow(unused_mut)] let mut res = Autolaunch::default(); @@ -80,12 +50,58 @@ impl<'a> TryFrom<&'a Address<'a>> for Autolaunch<'a> { Ok(res) } -} -impl KeyValFmtAdd for Autolaunch<'_> { - fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { + fn fmt_key_val<'s: 'b, 'b>(&'s self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { #[cfg(target_os = "windows")] let kv = kv.add("scope", self.scope()); kv } } + +/// Scope of autolaunch (Windows only) +#[cfg(target_os = "windows")] +#[derive(Clone, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum AutolaunchScope<'a> { + /// Limit session bus to dbus installation path. + InstallPath, + /// Limit session bus to the recent user. + User, + /// other values - specify dedicated session bus like "release", "debug" or other. + Other(Cow<'a, str>), +} + +#[cfg(target_os = "windows")] +impl<'a> AutolaunchScope<'a> { + fn into_owned(self) -> AutolaunchScope<'static> { + match self { + AutolaunchScope::InstallPath => AutolaunchScope::InstallPath, + AutolaunchScope::User => AutolaunchScope::User, + AutolaunchScope::Other(other) => AutolaunchScope::Other(other.into_owned().into()), + } + } +} + +#[cfg(target_os = "windows")] +impl fmt::Display for AutolaunchScope<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InstallPath => write!(f, "*install-path"), + Self::User => write!(f, "*user"), + Self::Other(o) => write!(f, "{o}"), + } + } +} + +#[cfg(target_os = "windows")] +impl<'a> TryFrom> for AutolaunchScope<'a> { + type Error = super::Error; + + fn try_from(s: Cow<'a, str>) -> Result { + match s.as_ref() { + "*install-path" => Ok(Self::InstallPath), + "*user" => Ok(Self::User), + _ => Ok(Self::Other(s)), + } + } +} diff --git a/zbus/src/address/transport/launchd.rs b/zbus/src/address/transport/launchd.rs index 4f44486eb..882aa58b0 100644 --- a/zbus/src/address/transport/launchd.rs +++ b/zbus/src/address/transport/launchd.rs @@ -1,11 +1,11 @@ use std::borrow::Cow; -use super::{percent::decode_percents_str, Address, Error, KeyValFmt, KeyValFmtAdd, Result}; +use super::{percent::decode_percents_str, Address, Error, KeyValFmt, Result, TransportImpl}; /// `launchd:` D-Bus transport. /// /// -#[derive(Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Launchd<'a> { env: Cow<'a, str>, } @@ -18,12 +18,17 @@ impl<'a> Launchd<'a> { pub fn env(&self) -> &str { self.env.as_ref() } -} -impl<'a> TryFrom<&'a Address<'a>> for Launchd<'a> { - type Error = Error; + /// Convert into owned version, with 'static lifetime. + pub fn into_owned(self) -> Launchd<'static> { + Launchd { + env: self.env.into_owned().into(), + } + } +} - fn try_from(s: &'a Address<'a>) -> Result { +impl<'a> TransportImpl<'a> for Launchd<'a> { + fn for_address(s: &'a Address<'a>) -> Result { for (k, v) in s.key_val_iter() { match (k, v) { ("env", Some(v)) => { @@ -37,10 +42,8 @@ impl<'a> TryFrom<&'a Address<'a>> for Launchd<'a> { Err(Error::MissingKey("env".into())) } -} -impl KeyValFmtAdd for Launchd<'_> { - fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { + fn fmt_key_val<'s: 'b, 'b>(&'s self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { kv.add("env", Some(self.env())) } } diff --git a/zbus/src/address/transport/mod.rs b/zbus/src/address/transport/mod.rs index 3d8a2585d..6d1e4fe8d 100644 --- a/zbus/src/address/transport/mod.rs +++ b/zbus/src/address/transport/mod.rs @@ -2,7 +2,7 @@ use std::fmt; -use super::{percent, Address, Error, KeyValFmt, KeyValFmtAdd, Result}; +use super::{percent, Address, Error, KeyValFmt, Result}; mod autolaunch; pub use autolaunch::Autolaunch; @@ -35,7 +35,7 @@ mod vsock; pub use vsock::Vsock; /// A D-Bus transport. -#[derive(Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] #[non_exhaustive] pub enum Transport<'a> { /// Unix Domain Sockets transport. @@ -58,6 +58,24 @@ pub enum Transport<'a> { Vsock(vsock::Vsock<'a>), } +impl<'a> Transport<'a> { + /// Convert into owned version, with 'static lifetime. + pub fn into_owned(self) -> Transport<'static> { + match self { + Transport::Unix(unix) => Transport::Unix(unix.into_owned()), + #[cfg(target_os = "macos")] + Transport::Launchd(launchd) => Transport::Launchd(launchd.into_owned()), + #[cfg(target_os = "linux")] + Transport::Systemd(systemd) => Transport::Systemd(systemd.into_owned()), + Transport::Tcp(tcp) => Transport::Tcp(tcp.into_owned()), + Transport::NonceTcp(nonce_tcp) => Transport::NonceTcp(nonce_tcp.into_owned()), + Transport::Unixexec(unixexec) => Transport::Unixexec(unixexec.into_owned()), + Transport::Autolaunch(autolaunch) => Transport::Autolaunch(autolaunch.into_owned()), + Transport::Vsock(vsock) => Transport::Vsock(vsock.into_owned()), + } + } +} + impl fmt::Display for Transport<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -75,40 +93,45 @@ impl fmt::Display for Transport<'_> { } } -impl KeyValFmtAdd for Transport<'_> { - fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { - match self { - Self::Unix(t) => t.key_val_fmt_add(kv), - #[cfg(target_os = "macos")] - Self::Launchd(t) => t.key_val_fmt_add(kv), - #[cfg(target_os = "linux")] - Self::Systemd(t) => t.key_val_fmt_add(kv), - Self::Tcp(t) => t.key_val_fmt_add(kv), - Self::NonceTcp(t) => t.key_val_fmt_add(kv), - Self::Unixexec(t) => t.key_val_fmt_add(kv), - Self::Autolaunch(t) => t.key_val_fmt_add(kv), - Self::Vsock(t) => t.key_val_fmt_add(kv), - } - } -} +pub(crate) trait TransportImpl<'a> { + fn for_address(s: &'a Address<'a>) -> Result + where + Self: Sized; -impl<'a> TryFrom<&'a Address<'a>> for Transport<'a> { - type Error = Error; + // add() the transport (key,val) pairs to KeyValFmt + fn fmt_key_val<'s: 'b, 'b>(&'s self, kv: KeyValFmt<'b>) -> KeyValFmt<'b>; +} - fn try_from(s: &'a Address<'a>) -> Result { +impl<'a> TransportImpl<'a> for Transport<'a> { + fn for_address(s: &'a Address<'a>) -> Result { let col = s.addr.find(':').ok_or(Error::MissingTransport)?; match &s.addr[..col] { - "unix" => Ok(Self::Unix(s.try_into()?)), + "unix" => Ok(Self::Unix(Unix::for_address(s)?)), #[cfg(target_os = "macos")] - "launchd" => Ok(Self::Launchd(s.try_into()?)), + "launchd" => Ok(Self::Launchd(Launchd::for_address(s)?)), #[cfg(target_os = "linux")] - "systemd" => Ok(Self::Systemd(s.try_into()?)), - "tcp" => Ok(Self::Tcp(s.try_into()?)), - "nonce-tcp" => Ok(Self::NonceTcp(s.try_into()?)), - "unixexec" => Ok(Self::Unixexec(s.try_into()?)), - "autolaunch" => Ok(Self::Autolaunch(s.try_into()?)), - "vsock" => Ok(Self::Vsock(s.try_into()?)), + "systemd" => Ok(Self::Systemd(Systemd::for_address(s)?)), + "tcp" => Ok(Self::Tcp(Tcp::for_address(s)?)), + "nonce-tcp" => Ok(Self::NonceTcp(NonceTcp::for_address(s)?)), + "unixexec" => Ok(Self::Unixexec(Unixexec::for_address(s)?)), + "autolaunch" => Ok(Self::Autolaunch(Autolaunch::for_address(s)?)), + "vsock" => Ok(Self::Vsock(Vsock::for_address(s)?)), _ => Err(Error::UnknownTransport), } } + + fn fmt_key_val<'s: 'b, 'b>(&'s self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { + match self { + Self::Unix(t) => t.fmt_key_val(kv), + #[cfg(target_os = "macos")] + Self::Launchd(t) => t.fmt_key_val(kv), + #[cfg(target_os = "linux")] + Self::Systemd(t) => t.fmt_key_val(kv), + Self::Tcp(t) => t.fmt_key_val(kv), + Self::NonceTcp(t) => t.fmt_key_val(kv), + Self::Unixexec(t) => t.fmt_key_val(kv), + Self::Autolaunch(t) => t.fmt_key_val(kv), + Self::Vsock(t) => t.fmt_key_val(kv), + } + } } diff --git a/zbus/src/address/transport/nonce_tcp.rs b/zbus/src/address/transport/nonce_tcp.rs index f0240f50d..78755d31b 100644 --- a/zbus/src/address/transport/nonce_tcp.rs +++ b/zbus/src/address/transport/nonce_tcp.rs @@ -3,13 +3,13 @@ use std::{borrow::Cow, ffi::OsStr}; use super::{ percent::{decode_percents_os_str, decode_percents_str, EncOsStr}, tcp::TcpFamily, - Address, Error, KeyValFmt, KeyValFmtAdd, Result, + Address, Error, KeyValFmt, Result, TransportImpl, }; /// `nonce-tcp:` D-Bus transport. /// /// -#[derive(Debug, Default, PartialEq, Eq)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct NonceTcp<'a> { host: Option>, bind: Option>, @@ -53,22 +53,29 @@ impl<'a> NonceTcp<'a> { pub fn noncefile(&self) -> Option<&OsStr> { self.noncefile.as_ref().map(|v| v.as_ref()) } -} -impl KeyValFmtAdd for NonceTcp<'_> { - fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { - kv.add("host", self.host()) - .add("bind", self.bind()) - .add("port", self.port()) - .add("family", self.family()) - .add("noncefile", self.noncefile().map(EncOsStr)) + /// Convert into owned version, with 'static lifetime. + pub fn into_owned(self) -> NonceTcp<'static> { + let Self { + host, + bind, + port, + family, + noncefile, + } = self; + + NonceTcp { + host: host.map(|h| h.into_owned().into()), + bind: bind.map(|b| b.into_owned().into()), + port, + family, + noncefile: noncefile.map(|n| n.into_owned().into()), + } } } -impl<'a> TryFrom<&'a Address<'a>> for NonceTcp<'a> { - type Error = Error; - - fn try_from(s: &'a Address<'a>) -> Result { +impl<'a> TransportImpl<'a> for NonceTcp<'a> { + fn for_address(s: &'a Address<'a>) -> Result { let mut res = NonceTcp::default(); for (k, v) in s.key_val_iter() { match (k, v) { @@ -97,4 +104,12 @@ impl<'a> TryFrom<&'a Address<'a>> for NonceTcp<'a> { Ok(res) } + + fn fmt_key_val<'s: 'b, 'b>(&'s self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { + kv.add("host", self.host()) + .add("bind", self.bind()) + .add("port", self.port()) + .add("family", self.family()) + .add("noncefile", self.noncefile().map(EncOsStr)) + } } diff --git a/zbus/src/address/transport/systemd.rs b/zbus/src/address/transport/systemd.rs index 5f6350f86..65b5f566c 100644 --- a/zbus/src/address/transport/systemd.rs +++ b/zbus/src/address/transport/systemd.rs @@ -1,28 +1,33 @@ use std::marker::PhantomData; -use super::{Address, Error, KeyValFmt, KeyValFmtAdd, Result}; +use super::{Address, KeyValFmt, Result, TransportImpl}; /// `systemd:` D-Bus transport. /// /// -#[derive(Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Systemd<'a> { // use a phantom lifetime for eventually future fields and consistency phantom: PhantomData<&'a ()>, } -impl<'a> TryFrom<&'a Address<'a>> for Systemd<'a> { - type Error = Error; +impl<'a> Systemd<'a> { + /// Convert into owned version, with 'static lifetime. + pub fn into_owned(&self) -> Systemd<'static> { + Systemd { + phantom: PhantomData, + } + } +} - fn try_from(_s: &'a Address<'a>) -> Result { +impl<'a> TransportImpl<'a> for Systemd<'a> { + fn for_address(_addr: &'a Address<'a>) -> Result { Ok(Systemd { phantom: PhantomData, }) } -} -impl KeyValFmtAdd for Systemd<'_> { - fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { + fn fmt_key_val<'s: 'b, 'b>(&'s self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { kv } } diff --git a/zbus/src/address/transport/tcp.rs b/zbus/src/address/transport/tcp.rs index 2f1969e2d..203fa818f 100644 --- a/zbus/src/address/transport/tcp.rs +++ b/zbus/src/address/transport/tcp.rs @@ -1,42 +1,11 @@ use std::{borrow::Cow, fmt}; -use super::{percent::decode_percents_str, Address, Error, KeyValFmt, KeyValFmtAdd, Result}; - -/// TCP IP address family -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[non_exhaustive] -pub enum TcpFamily { - /// IPv4 - IPv4, - /// IPv6 - IPv6, -} - -impl fmt::Display for TcpFamily { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::IPv4 => write!(f, "ipv4"), - Self::IPv6 => write!(f, "ipv6"), - } - } -} - -impl TryFrom<&str> for TcpFamily { - type Error = Error; - - fn try_from(s: &str) -> Result { - match s { - "ipv4" => Ok(Self::IPv4), - "ipv6" => Ok(Self::IPv6), - _ => Err(Error::UnknownTcpFamily(s.into())), - } - } -} +use super::{percent::decode_percents_str, Address, Error, KeyValFmt, Result, TransportImpl}; /// `tcp:` D-Bus transport. /// /// -#[derive(Debug, Default, PartialEq, Eq)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct Tcp<'a> { host: Option>, bind: Option>, @@ -72,21 +41,20 @@ impl<'a> Tcp<'a> { pub fn family(&self) -> Option { self.family } -} -impl KeyValFmtAdd for Tcp<'_> { - fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { - kv.add("host", self.host()) - .add("bind", self.bind()) - .add("port", self.port()) - .add("family", self.family()) + /// Convert into owned version, with 'static lifetime. + pub fn into_owned(self) -> Tcp<'static> { + Tcp { + host: self.host.map(|h| h.into_owned().into()), + bind: self.bind.map(|b| b.into_owned().into()), + port: self.port, + family: self.family, + } } } -impl<'a> TryFrom<&'a Address<'a>> for Tcp<'a> { - type Error = Error; - - fn try_from(s: &'a Address<'a>) -> Result { +impl<'a> TransportImpl<'a> for Tcp<'a> { + fn for_address(s: &'a Address<'a>) -> Result { let mut res = Tcp::default(); for (k, v) in s.key_val_iter() { match (k, v) { @@ -112,4 +80,42 @@ impl<'a> TryFrom<&'a Address<'a>> for Tcp<'a> { Ok(res) } + + fn fmt_key_val<'s: 'b, 'b>(&'s self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { + kv.add("host", self.host()) + .add("bind", self.bind()) + .add("port", self.port()) + .add("family", self.family()) + } +} + +/// TCP IP address family +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] +pub enum TcpFamily { + /// IPv4 + IPv4, + /// IPv6 + IPv6, +} + +impl fmt::Display for TcpFamily { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::IPv4 => write!(f, "ipv4"), + Self::IPv6 => write!(f, "ipv6"), + } + } +} + +impl TryFrom<&str> for TcpFamily { + type Error = Error; + + fn try_from(s: &str) -> Result { + match s { + "ipv4" => Ok(Self::IPv4), + "ipv6" => Ok(Self::IPv6), + _ => Err(Error::UnknownTcpFamily(s.into())), + } + } } diff --git a/zbus/src/address/transport/unix.rs b/zbus/src/address/transport/unix.rs index d105ce18b..d8d0f2bf4 100644 --- a/zbus/src/address/transport/unix.rs +++ b/zbus/src/address/transport/unix.rs @@ -2,45 +2,13 @@ use std::{borrow::Cow, ffi::OsStr}; use super::{ percent::{decode_percents, decode_percents_os_str, decode_percents_str, EncData, EncOsStr}, - Address, Error, KeyValFmt, KeyValFmtAdd, Result, + Address, Error, KeyValFmt, Result, TransportImpl, }; -/// A sub-type of `unix:` transport. -#[derive(Debug, PartialEq, Eq)] -#[non_exhaustive] -pub enum UnixAddrKind<'a> { - /// Path of the unix domain socket. - Path(Cow<'a, OsStr>), - /// Directory in which a socket file with a random file name starting with 'dbus-' should be - /// created by a server. - Dir(Cow<'a, OsStr>), - /// The same as "dir", except that on platforms with abstract sockets, a server may attempt to - /// create an abstract socket whose name starts with this directory instead of a path-based - /// socket. - Tmpdir(Cow<'a, OsStr>), - /// Unique string in the abstract namespace, often syntactically resembling a path but - /// unconnected to the filesystem namespace - Abstract(Cow<'a, [u8]>), - /// Listen on $XDG_RUNTIME_DIR/bus. - Runtime, -} - -impl KeyValFmtAdd for UnixAddrKind<'_> { - fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { - match self { - UnixAddrKind::Path(p) => kv.add("path", Some(EncOsStr(p))), - UnixAddrKind::Dir(p) => kv.add("dir", Some(EncOsStr(p))), - UnixAddrKind::Tmpdir(p) => kv.add("tmpdir", Some(EncOsStr(p))), - UnixAddrKind::Abstract(p) => kv.add("abstract", Some(EncData(p))), - UnixAddrKind::Runtime => kv.add("runtime", Some("yes")), - } - } -} - /// `unix:` D-Bus transport. /// /// -#[derive(Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Unix<'a> { kind: UnixAddrKind<'a>, } @@ -50,12 +18,17 @@ impl<'a> Unix<'a> { pub fn kind(&self) -> &UnixAddrKind<'a> { &self.kind } -} -impl<'a> TryFrom<&'a Address<'a>> for Unix<'a> { - type Error = Error; + /// Convert into owned version, with 'static lifetime. + pub fn into_owned(self) -> Unix<'static> { + Unix { + kind: self.kind.into_owned(), + } + } +} - fn try_from(s: &'a Address<'a>) -> Result { +impl<'a> TransportImpl<'a> for Unix<'a> { + fn for_address(s: &'a Address<'a>) -> Result { let mut kind = None; let mut iter = s.key_val_iter(); for (k, v) in &mut iter { @@ -109,10 +82,50 @@ impl<'a> TryFrom<&'a Address<'a>> for Unix<'a> { Ok(Unix { kind }) } + + fn fmt_key_val<'s: 'b, 'b>(&'s self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { + self.kind().fmt_key_val(kv) + } +} + +/// A sub-type of `unix:` transport. +#[derive(Clone, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum UnixAddrKind<'a> { + /// Path of the unix domain socket. + Path(Cow<'a, OsStr>), + /// Directory in which a socket file with a random file name starting with 'dbus-' should be + /// created by a server. + Dir(Cow<'a, OsStr>), + /// The same as "dir", except that on platforms with abstract sockets, a server may attempt to + /// create an abstract socket whose name starts with this directory instead of a path-based + /// socket. + Tmpdir(Cow<'a, OsStr>), + /// Unique string in the abstract namespace, often syntactically resembling a path but + /// unconnected to the filesystem namespace + Abstract(Cow<'a, [u8]>), + /// Listen on $XDG_RUNTIME_DIR/bus. + Runtime, } -impl KeyValFmtAdd for Unix<'_> { - fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { - self.kind().key_val_fmt_add(kv) +impl UnixAddrKind<'_> { + fn fmt_key_val<'s: 'b, 'b>(&'s self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { + match self { + UnixAddrKind::Path(p) => kv.add("path", Some(EncOsStr(p))), + UnixAddrKind::Dir(p) => kv.add("dir", Some(EncOsStr(p))), + UnixAddrKind::Tmpdir(p) => kv.add("tmpdir", Some(EncOsStr(p))), + UnixAddrKind::Abstract(p) => kv.add("abstract", Some(EncData(p))), + UnixAddrKind::Runtime => kv.add("runtime", Some("yes")), + } + } + + fn into_owned(self) -> UnixAddrKind<'static> { + match self { + UnixAddrKind::Path(cow) => UnixAddrKind::Path(cow.into_owned().into()), + UnixAddrKind::Dir(cow) => UnixAddrKind::Dir(cow.into_owned().into()), + UnixAddrKind::Tmpdir(cow) => UnixAddrKind::Tmpdir(cow.into_owned().into()), + UnixAddrKind::Abstract(cow) => UnixAddrKind::Abstract(cow.into_owned().into()), + UnixAddrKind::Runtime => UnixAddrKind::Runtime, + } } } diff --git a/zbus/src/address/transport/unixexec.rs b/zbus/src/address/transport/unixexec.rs index a74137049..77576b35c 100644 --- a/zbus/src/address/transport/unixexec.rs +++ b/zbus/src/address/transport/unixexec.rs @@ -2,24 +2,13 @@ use std::{borrow::Cow, ffi::OsStr, fmt}; use super::{ percent::{decode_percents_os_str, decode_percents_str, EncOsStr}, - Address, Error, KeyValFmt, KeyValFmtAdd, Result, + Address, Error, KeyValFmt, Result, TransportImpl, }; -#[derive(Debug, PartialEq, Eq)] -struct Argv(usize); - -impl fmt::Display for Argv { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let n = self.0; - - write!(f, "argv{n}") - } -} - /// `unixexec:` D-Bus transport. /// /// -#[derive(Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Unixexec<'a> { path: Cow<'a, OsStr>, argv: Vec<(usize, Cow<'a, str>)>, @@ -41,12 +30,23 @@ impl<'a> Unixexec<'a> { pub fn argv(&self) -> &[(usize, Cow<'a, str>)] { self.argv.as_ref() } -} -impl<'a> TryFrom<&'a Address<'a>> for Unixexec<'a> { - type Error = Error; + /// Convert into owned version, with 'static lifetime. + pub fn into_owned(self) -> Unixexec<'static> { + let argv = self + .argv + .into_iter() + .map(|(index, cow)| (index, cow.into_owned().into())) + .collect(); + Unixexec { + path: self.path.into_owned().into(), + argv, + } + } +} - fn try_from(s: &'a Address<'a>) -> Result { +impl<'a> TransportImpl<'a> for Unixexec<'a> { + fn for_address(s: &'a Address<'a>) -> Result { let mut path = None; let mut argv = Vec::new(); @@ -72,10 +72,8 @@ impl<'a> TryFrom<&'a Address<'a>> for Unixexec<'a> { Ok(Self { path, argv }) } -} -impl KeyValFmtAdd for Unixexec<'_> { - fn key_val_fmt_add<'a: 'b, 'b>(&'a self, mut kv: KeyValFmt<'b>) -> KeyValFmt<'b> { + fn fmt_key_val<'s: 'b, 'b>(&'s self, mut kv: KeyValFmt<'b>) -> KeyValFmt<'b> { kv = kv.add("path", Some(EncOsStr(self.path()))); for (n, arg) in self.argv() { kv = kv.add(Argv(*n), Some(arg)); @@ -84,3 +82,14 @@ impl KeyValFmtAdd for Unixexec<'_> { kv } } + +#[derive(Debug, PartialEq, Eq)] +struct Argv(usize); + +impl fmt::Display for Argv { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let n = self.0; + + write!(f, "argv{n}") + } +} diff --git a/zbus/src/address/transport/vsock.rs b/zbus/src/address/transport/vsock.rs index a160f00ba..eb3b347e8 100644 --- a/zbus/src/address/transport/vsock.rs +++ b/zbus/src/address/transport/vsock.rs @@ -1,9 +1,9 @@ use std::marker::PhantomData; -use super::{percent::decode_percents_str, Address, Error, KeyValFmt, KeyValFmtAdd, Result}; +use super::{percent::decode_percents_str, Address, Error, KeyValFmt, Result, TransportImpl}; /// `vsock:` D-Bus transport. -#[derive(Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Vsock<'a> { // no cid means ANY cid: Option, @@ -23,12 +23,19 @@ impl<'a> Vsock<'a> { pub fn cid(&self) -> Option { self.cid } -} -impl<'a> TryFrom<&'a Address<'a>> for Vsock<'a> { - type Error = Error; + /// Convert into owned version, with 'static lifetime. + pub fn into_owned(self) -> Vsock<'static> { + Vsock { + cid: self.cid, + port: self.port, + phantom: PhantomData, + } + } +} - fn try_from(s: &'a Address<'a>) -> Result { +impl<'a> TransportImpl<'a> for Vsock<'a> { + fn for_address(s: &'a Address<'a>) -> Result { let mut port = None; let mut cid = None; @@ -58,10 +65,8 @@ impl<'a> TryFrom<&'a Address<'a>> for Vsock<'a> { phantom: PhantomData, }) } -} -impl KeyValFmtAdd for Vsock<'_> { - fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { + fn fmt_key_val<'s: 'b, 'b>(&'s self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { kv.add("cid", self.cid()).add("port", self.port()) } } diff --git a/zbus/src/blocking/connection/builder.rs b/zbus/src/blocking/connection/builder.rs index 4efd47cbf..5bb2e3a9f 100644 --- a/zbus/src/blocking/connection/builder.rs +++ b/zbus/src/blocking/connection/builder.rs @@ -15,7 +15,7 @@ use zvariant::ObjectPath; #[cfg(feature = "p2p")] use crate::Guid; use crate::{ - address::ToAddresses, blocking::Connection, conn::AuthMechanism, + address::ToOwnedAddresses, blocking::Connection, conn::AuthMechanism, connection::socket::BoxedSplit, names::WellKnownName, object_server::Interface, utils::block_on, Error, Result, }; @@ -43,7 +43,7 @@ impl<'a> Builder<'a> { /// [D-Bus bus address]: https://dbus.freedesktop.org/doc/dbus-specification.html#addresses pub fn address<'t, A>(address: &'t A) -> Result where - A: ToAddresses<'t> + ?Sized, + A: ToOwnedAddresses<'t> + ?Sized, { crate::connection::Builder::address(address).map(Self) } diff --git a/zbus/src/connection/builder.rs b/zbus/src/connection/builder.rs index 78218efe8..1295e9d69 100644 --- a/zbus/src/connection/builder.rs +++ b/zbus/src/connection/builder.rs @@ -24,7 +24,7 @@ use vsock::VsockStream; use zvariant::ObjectPath; use crate::{ - address::{Address, ToAddresses}, + address::{OwnedAddress, ToOwnedAddresses}, names::{InterfaceName, WellKnownName}, object_server::{ArcInterface, Interface}, Connection, Error, Executor, Guid, OwnedGuid, Result, @@ -48,7 +48,7 @@ enum Target { feature = "tokio-vsock" ))] VsockStream(VsockStream), - Address(Vec>), + Address(Vec), Socket(Split, Box>), AuthenticatedSocket(Split, Box>), } @@ -119,12 +119,11 @@ impl<'a> Builder<'a> { /// [D-Bus bus address]: https://dbus.freedesktop.org/doc/dbus-specification.html#addresses pub fn address<'t, A>(address: &'t A) -> Result where - A: ToAddresses<'t> + ?Sized, + A: ToOwnedAddresses<'t> + ?Sized, { let addr = address - .to_addresses() + .to_owned_addresses() .filter_map(std::result::Result::ok) - .map(|a| a.to_owned()) .collect(); Ok(Builder::new(Target::Address(addr))) @@ -492,7 +491,7 @@ impl<'a> Builder<'a> { #[cfg(feature = "tokio-vsock")] Target::VsockStream(stream) => stream.into(), Target::Address(address) => { - return connect_address(&address) + return connect_address(address.as_slice()) .await .map(|(split, guid)| (split, guid, false)); } diff --git a/zbus/src/connection/connect/macos.rs b/zbus/src/connection/connect/macos.rs index 21db9b20c..85e7bbc63 100644 --- a/zbus/src/connection/connect/macos.rs +++ b/zbus/src/connection/connect/macos.rs @@ -2,7 +2,7 @@ use super::socket; use crate::{ - address::{transport::Transport, Address}, + address::{self, transport::Transport, Address}, process::run, Error, Result, }; @@ -13,14 +13,15 @@ async fn launchd_bus_address(env_key: &str) -> Result> { .expect("failed to wait on launchctl output"); if !output.status.success() { - return Err(Error::Address(format!( + return Err(Error::Failure(format!( "launchctl terminated with code: {}", output.status ))); } - let addr = String::from_utf8(output.stdout) - .map_err(|e| Error::Address(format!("Unable to parse launchctl output as UTF-8: {}", e)))?; + let addr = String::from_utf8(output.stdout).map_err(|e| { + address::Error::Encoding(format!("Unable to parse launchctl output as UTF-8: {}", e)) + })?; Ok(format!("unix:path={}", addr.trim()).try_into()?) } @@ -32,7 +33,10 @@ pub(crate) async fn connect( match addr.transport()? { Transport::Unix(t) => socket::unix::connect(&t).await, - _ => Err(Error::Address(format!("Address is unsupported: {}", addr))), + _ => { + tracing::debug!("Address is unsupported: {}", addr); + Err(Error::Unsupported) + } } } diff --git a/zbus/src/connection/connect/mod.rs b/zbus/src/connection/connect/mod.rs index 34e25b9f6..4ca9e8d0f 100644 --- a/zbus/src/connection/connect/mod.rs +++ b/zbus/src/connection/connect/mod.rs @@ -1,8 +1,8 @@ use tracing::debug; use crate::{ - address::{transport::Transport, Address}, - Error, Guid, OwnedGuid, Result, + address::{transport::Transport, OwnedAddress}, + Error, OwnedGuid, Result, }; use super::socket::{self, BoxedSplit}; @@ -11,7 +11,7 @@ mod macos; mod win32; pub(crate) async fn connect_address( - address: &[Address<'_>], + address: &[OwnedAddress], ) -> Result<(BoxedSplit, Option)> { for addr in address { match connect(addr).await { @@ -24,33 +24,30 @@ pub(crate) async fn connect_address( } } } - Err(Error::Address("No connectable address".into())) + Err(Error::Failure("No connectable address".into())) } -async fn connect(addr: &Address<'_>) -> ConnectResult { - let addr = addr.to_owned(); - let guid = match addr.guid() { - Some(g) => Some(Guid::try_from(g.as_ref())?.into()), - _ => None, - }; - let split = match addr.transport()? { - Transport::Tcp(t) => socket::tcp::connect(&t).await?.into(), - Transport::NonceTcp(t) => socket::tcp::connect_nonce(&t).await?.into(), +async fn connect(addr: &OwnedAddress) -> ConnectResult { + let guid = addr.guid().map(OwnedGuid::from); + let split = match addr.transport() { + Transport::Tcp(t) => socket::tcp::connect(t).await?.into(), + Transport::NonceTcp(t) => socket::tcp::connect_nonce(t).await?.into(), #[cfg(any(unix, not(feature = "tokio")))] - Transport::Unix(u) => socket::unix::connect(&u).await?.into(), + Transport::Unix(u) => socket::unix::connect(u).await?.into(), #[cfg(any( all(feature = "vsock", not(feature = "tokio")), feature = "tokio-vsock" ))] - Transport::Vsock(v) => socket::vsock::connect(&v).await?.into(), + Transport::Vsock(v) => socket::vsock::connect(v).await?.into(), #[cfg(target_os = "macos")] - Transport::Launchd(l) => macos::connect(&l).await?.into(), + Transport::Launchd(l) => macos::connect(l).await?.into(), #[cfg(target_os = "windows")] Transport::Autolaunch(l) => { - return win32::connect(&l).await; + return win32::connect(l).await; } _ => { - return Err(Error::Address(format!("Unhandled address: {}", addr))); + tracing::debug!("Unsupported address: {addr}"); + return Err(Error::Unsupported); } }; Ok((split, guid)) diff --git a/zbus/src/connection/connect/win32.rs b/zbus/src/connection/connect/win32.rs index eea290a43..72096fc59 100644 --- a/zbus/src/connection/connect/win32.rs +++ b/zbus/src/connection/connect/win32.rs @@ -2,26 +2,25 @@ use super::ConnectResult; use crate::{ - address::{transport::Transport, Address}, + address::{transport::Transport, OwnedAddress}, win32::autolaunch_bus_address, Error, }; use std::{future::Future, pin::Pin}; pub(crate) fn connect<'l>( - l: &'l crate::address::transport::Autolaunch<'_>, + autolaunch: &'l crate::address::transport::Autolaunch<'_>, ) -> Pin + 'l>> { Box::pin(async move { - if l.scope().is_some() { - return Err(Error::Address( - "autolaunch with scope isn't supported yet".into(), - )); + if autolaunch.scope().is_some() { + tracing::debug!("autolaunch with scope isn't supported yet"); + return Err(Error::Unsupported); } - let addr: Address<'_> = autolaunch_bus_address()?.try_into()?; + let addr: OwnedAddress = autolaunch_bus_address()?.try_into()?; - if let Transport::Autolaunch(_) = addr.transport()? { - return Err(Error::Address("Recursive autolaunch: address".into())); + if let Transport::Autolaunch(_) = addr.transport() { + return Err(Error::Failure("Recursive autolaunch: address".into())); } super::connect(&addr).await diff --git a/zbus/src/connection/socket/tcp.rs b/zbus/src/connection/socket/tcp.rs index 80c62a0da..7616bc9aa 100644 --- a/zbus/src/connection/socket/tcp.rs +++ b/zbus/src/connection/socket/tcp.rs @@ -6,7 +6,10 @@ use std::os::fd::BorrowedFd; #[cfg(not(feature = "tokio"))] use std::{net::TcpStream, sync::Arc}; -use crate::{address::transport::TcpFamily, Error, Result}; +use crate::{ + address::{self, transport::TcpFamily}, + Result, +}; use super::{ReadHalf, RecvmsgResult, WriteHalf}; #[cfg(feature = "tokio")] @@ -211,19 +214,18 @@ async fn connect_with(host: &str, port: u16, family: Option) -> Resul }, "connect tcp", ) - .await - .map_err(|e| Error::Address(format!("Failed to receive TCP addresses: {e}")))?; + .await?; // we could attempt connections in parallel? - let mut last_err = Error::Address("Failed to connect".into()); + let mut last_err = io::Error::other("No address"); for addr in addrs { match Stream::connect(addr).await { Ok(stream) => return Ok(stream), - Err(e) => last_err = e.into(), + Err(e) => last_err = e, } } - Err(last_err) + Err(last_err.into()) } #[cfg(feature = "tokio")] @@ -232,16 +234,16 @@ async fn connect_with(host: &str, port: u16, family: Option) -> Resul let _ = family; Stream::connect((host, port)) .await - .map_err(|e| Error::InputOutput(e.into())) + .map_err(|e| crate::Error::InputOutput(e.into())) } } pub(crate) async fn connect(addr: &crate::address::transport::Tcp<'_>) -> Result { let Some(host) = addr.host() else { - return Err(Error::Address("No host in address".into())); + return Err(address::Error::MissingValue("host".into()).into()); }; let Some(port) = addr.port() else { - return Err(Error::Address("No port in address".into())); + return Err(address::Error::MissingValue("port".into()).into()); }; connect_with(host, port, addr.family()).await @@ -251,13 +253,13 @@ pub(crate) async fn connect_nonce( addr: &crate::address::transport::NonceTcp<'_>, ) -> Result { let Some(host) = addr.host() else { - return Err(Error::Address("No host in address".into())); + return Err(address::Error::MissingValue("host".into()).into()); }; let Some(port) = addr.port() else { - return Err(Error::Address("No port in address".into())); + return Err(address::Error::MissingValue("port".into()).into()); }; let Some(noncefile) = addr.noncefile() else { - return Err(Error::Address("No noncefile in address".into())); + return Err(address::Error::MissingValue("noncefile".into()).into()); }; #[allow(unused_mut)] diff --git a/zbus/src/connection/socket/unix.rs b/zbus/src/connection/socket/unix.rs index 72edff430..48c212800 100644 --- a/zbus/src/connection/socket/unix.rs +++ b/zbus/src/connection/socket/unix.rs @@ -405,7 +405,7 @@ pub(crate) async fn connect(addr: &crate::address::transport::Unix<'_>) -> Resul UnixAddrKind::Path(p) => p.clone().into_owned(), #[cfg(target_os = "linux")] UnixAddrKind::Abstract(name) => SocketAddr::from_abstract_name(name)?, - _ => return Err(Error::Address("Address is not connectable".into())), + _ => return Err(Error::Failure("Address is not connectable".into())), }; let stream = crate::Task::spawn_blocking( diff --git a/zbus/src/connection/socket/vsock.rs b/zbus/src/connection/socket/vsock.rs index 5b138a0e5..e1cc1aefc 100644 --- a/zbus/src/connection/socket/vsock.rs +++ b/zbus/src/connection/socket/vsock.rs @@ -133,11 +133,13 @@ type Stream = tokio_vsock::VsockStream; feature = "tokio-vsock" ))] pub(crate) async fn connect(addr: &crate::address::transport::Vsock<'_>) -> Result { + use crate::address; + let Some(cid) = addr.cid() else { - return Err(Error::Address("No cid in address".into())); + return Err(address::Error::MissingValue("cid".into()).into()); }; let Some(port) = addr.port() else { - return Err(Error::Address("No port in address".into())); + return Err(address::Error::MissingValue("port".into()).into()); }; #[cfg(all(feature = "vsock", not(feature = "tokio")))] @@ -146,8 +148,7 @@ pub(crate) async fn connect(addr: &crate::address::transport::Vsock<'_>) -> Resu move || vsock::VsockStream::connect_with_cid_port(cid, port), "connect vsock", ) - .await - .map_err(|e| Error::Address(format!("Failed to connect: {e}")))?; + .await?; Ok(Async::new(stream).map_err(|e| Error::InputOutput(e.into()))?) } diff --git a/zbus/src/error.rs b/zbus/src/error.rs index 4adb4ed30..3c0cf204b 100644 --- a/zbus/src/error.rs +++ b/zbus/src/error.rs @@ -4,7 +4,7 @@ use zbus_names::{Error as NamesError, InterfaceName, OwnedErrorName}; use zvariant::{Error as VariantError, ObjectPath}; use crate::{ - fdo, + address, fdo, message::{Message, Type}, }; @@ -18,7 +18,7 @@ pub enum Error { /// Interface not found. InterfaceNotFound, /// Invalid D-Bus address. - Address(String), + Address(address::Error), /// An I/O error. InputOutput(Arc), /// Invalid message field. @@ -68,7 +68,7 @@ assert_impl_all!(Error: Send, Sync, Unpin); impl PartialEq for Error { fn eq(&self, other: &Self) -> bool { match (self, other) { - (Self::Address(_), Self::Address(_)) => true, + (Self::Address(s), Self::Address(o)) => s == o, (Self::InterfaceNotFound, Self::InterfaceNotFound) => true, (Self::Handshake(_), Self::Handshake(_)) => true, (Self::InvalidReply, Self::InvalidReply) => true, @@ -97,7 +97,7 @@ impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { Error::InterfaceNotFound => None, - Error::Address(_) => None, + Error::Address(s) => Some(s), Error::InputOutput(e) => Some(e), Error::ExcessData => None, Error::Handshake(_) => None, @@ -125,7 +125,7 @@ impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Error::InterfaceNotFound => write!(f, "Interface not found"), - Error::Address(e) => write!(f, "address error: {e}"), + Error::Address(e) => write!(f, "{e}"), Error::ExcessData => write!(f, "excess data"), Error::InputOutput(e) => write!(f, "I/O error: {e}"), Error::Handshake(e) => write!(f, "D-Bus handshake failed: {e}"), @@ -199,9 +199,9 @@ impl From for Error { } } -impl From for Error { - fn from(val: crate::address::Error) -> Self { - Error::Address(val.to_string()) +impl From for Error { + fn from(val: address::Error) -> Self { + Error::Address(val) } } diff --git a/zbus/src/guid.rs b/zbus/src/guid.rs index 3e9ca5b2b..c0b9933c6 100644 --- a/zbus/src/guid.rs +++ b/zbus/src/guid.rs @@ -218,6 +218,12 @@ impl From for Str<'_> { } } +impl From<&crate::address::guid::Guid> for OwnedGuid { + fn from(value: &crate::address::guid::Guid) -> Self { + Guid(value.to_string().into()).into() + } +} + impl<'de> Deserialize<'de> for OwnedGuid { fn deserialize(deserializer: D) -> std::result::Result where diff --git a/zbus/src/win32.rs b/zbus/src/win32.rs index 2d4d47653..ec391a667 100644 --- a/zbus/src/win32.rs +++ b/zbus/src/win32.rs @@ -288,7 +288,7 @@ fn read_shm(name: &str) -> Result, crate::Error> { // SAFETY: We have exclusive ownership over the file mapping handle unsafe { OwnedHandle::from_raw_handle(res as RawHandle) } } else { - return Err(crate::Error::Address( + return Err(crate::Error::Failure( "Unable to open shared memory".to_owned(), )); } @@ -297,7 +297,7 @@ fn read_shm(name: &str) -> Result, crate::Error> { let addr = unsafe { MapViewOfFile(handle.as_raw_handle(), FILE_MAP_READ, 0, 0, 0) }; if addr.Value.is_null() { - return Err(crate::Error::Address("MapViewOfFile() failed".to_owned())); + return Err(crate::Error::Failure("MapViewOfFile() failed".to_owned())); } let data = unsafe { CStr::from_ptr(addr.Value.cast()) }; @@ -309,8 +309,9 @@ pub fn autolaunch_bus_address() -> Result { let _guard = mutex.lock(); let addr = read_shm("DBusDaemonAddressInfo")?; - let addr = String::from_utf8(addr) - .map_err(|e| crate::Error::Address(format!("Unable to parse address as UTF-8: {}", e)))?; + let addr = String::from_utf8(addr).map_err(|e| { + crate::address::Error::Encoding(format!("Unable to parse address as UTF-8: {}", e)) + })?; Ok(addr) }