dbus_addr/
lib.rs

1#![doc = include_str!("../README.md")]
2use std::{env, fmt};
3
4pub mod transport;
5
6mod address;
7pub use address::{DBusAddr, ToDBusAddrs};
8
9mod owned_address;
10pub use owned_address::{OwnedDBusAddr, ToOwnedDBusAddrs};
11
12mod address_list;
13pub use address_list::{DBusAddrList, DBusAddrListIter, OwnedDBusAddrListIter};
14
15mod percent;
16pub use percent::*;
17
18mod guid;
19pub use guid::Guid;
20
21#[cfg(test)]
22mod tests;
23
24/// Error returned when an address is invalid.
25#[derive(Debug, Clone, Eq, PartialEq)]
26pub enum Error {
27    MissingTransport,
28    Encoding(String),
29    DuplicateKey(String),
30    MissingKey(String),
31    MissingValue(String),
32    InvalidValue(String),
33    UnknownTcpFamily(String),
34    Other(String),
35}
36
37impl fmt::Display for Error {
38    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39        match self {
40            Error::MissingTransport => write!(f, "Missing transport in address"),
41            Error::Encoding(e) => write!(f, "Encoding error: {e}"),
42            Error::DuplicateKey(e) => write!(f, "Duplicate key: `{e}`"),
43            Error::MissingKey(e) => write!(f, "Missing key: `{e}`"),
44            Error::MissingValue(e) => write!(f, "Missing value for key: `{e}`"),
45            Error::InvalidValue(e) => write!(f, "Invalid value for key: `{e}`"),
46            Error::UnknownTcpFamily(e) => write!(f, "Unknown TCP address family: `{e}`"),
47            Error::Other(e) => write!(f, "Other error: {e}"),
48        }
49    }
50}
51
52impl std::error::Error for Error {}
53
54pub type Result<T> = std::result::Result<T, Error>;
55
56/// Get the address for session socket respecting the DBUS_SESSION_BUS_ADDRESS environment
57/// variable. If we don't recognize the value (or it's not set) we fall back to
58/// $XDG_RUNTIME_DIR/bus
59pub fn session() -> Result<DBusAddrList<'static>> {
60    match env::var("DBUS_SESSION_BUS_ADDRESS") {
61        Ok(val) => DBusAddrList::try_from(val),
62        _ => {
63            #[cfg(windows)]
64            {
65                DBusAddrList::try_from("autolaunch:scope=*user;autolaunch:")
66            }
67
68            #[cfg(all(unix, not(target_os = "macos")))]
69            {
70                #[link(name = "c")]
71                extern "C" {
72                    fn geteuid() -> u32;
73                }
74
75                let runtime_dir = env::var("XDG_RUNTIME_DIR")
76                    .unwrap_or_else(|_| format!("/run/user/{}", unsafe { geteuid() }));
77                let path = format!("unix:path={runtime_dir}/bus");
78
79                DBusAddrList::try_from(path)
80            }
81
82            #[cfg(target_os = "macos")]
83            {
84                DBusAddrList::try_from("launchd:env=DBUS_LAUNCHD_SESSION_BUS_SOCKET")
85            }
86        }
87    }
88}
89
90/// Get the address for system bus respecting the DBUS_SYSTEM_BUS_ADDRESS environment
91/// variable. If we don't recognize the value (or it's not set) we fall back to
92/// /var/run/dbus/system_bus_socket
93pub fn system() -> Result<DBusAddrList<'static>> {
94    match env::var("DBUS_SYSTEM_BUS_ADDRESS") {
95        Ok(val) => DBusAddrList::try_from(val),
96        _ => {
97            #[cfg(all(unix, not(target_os = "macos")))]
98            return DBusAddrList::try_from("unix:path=/var/run/dbus/system_bus_socket");
99
100            #[cfg(windows)]
101            return DBusAddrList::try_from("autolaunch:");
102
103            #[cfg(target_os = "macos")]
104            return DBusAddrList::try_from("launchd:env=DBUS_LAUNCHD_SESSION_BUS_SOCKET");
105        }
106    }
107}
108
109struct KeyValIter<'a> {
110    data: &'a str,
111    next_index: usize,
112}
113
114impl<'a> KeyValIter<'a> {
115    fn new(data: &'a str) -> Self {
116        KeyValIter {
117            data,
118            next_index: 0,
119        }
120    }
121}
122
123impl<'a> Iterator for KeyValIter<'a> {
124    type Item = (&'a str, Option<&'a str>);
125
126    fn next(&mut self) -> Option<Self::Item> {
127        if self.next_index >= self.data.len() {
128            return None;
129        }
130
131        let mut pair = &self.data[self.next_index..];
132        if let Some(end) = pair.find(',') {
133            pair = &pair[..end];
134            self.next_index += end + 1;
135        } else {
136            self.next_index = self.data.len();
137        }
138        let mut split = pair.split('=');
139        // SAFETY: first split always returns something
140        let key = split.next().unwrap();
141
142        Some((key, split.next()))
143    }
144}
145
146// A structure for formatting key-value pairs.
147//
148// This struct allows for the dynamic collection and formatting of key-value pairs,
149// where keys implement `fmt::Display` and values implement `Encodable`.
150pub(crate) struct KeyValFmt<'a> {
151    fields: Vec<(Box<dyn fmt::Display + 'a>, Box<dyn Encodable + 'a>)>,
152}
153
154impl<'a> KeyValFmt<'a> {
155    fn new() -> Self {
156        Self { fields: vec![] }
157    }
158
159    pub(crate) fn add<K, V>(mut self, key: K, val: Option<V>) -> Self
160    where
161        K: fmt::Display + 'a,
162        V: Encodable + 'a,
163    {
164        if let Some(val) = val {
165            self.fields.push((Box::new(key), Box::new(val)));
166        }
167
168        self
169    }
170}
171
172impl fmt::Display for KeyValFmt<'_> {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        let mut first = true;
175        for (k, v) in self.fields.iter() {
176            if !first {
177                write!(f, ",")?;
178            }
179            write!(f, "{k}=")?;
180            v.encode(f)?;
181            first = false;
182        }
183
184        Ok(())
185    }
186}