Skip to main content

zbus/
error.rs

1use std::{convert::Infallible, error, fmt, io, sync::Arc};
2use zbus_names::{Error as NamesError, InterfaceName, OwnedErrorName};
3use zvariant::{Error as VariantError, ObjectPath};
4
5use crate::{
6    Address, fdo,
7    message::{Message, Type},
8};
9
10/// The error type for `zbus`.
11///
12/// The various errors that can be reported by this crate.
13#[derive(Debug)]
14#[non_exhaustive]
15#[allow(clippy::upper_case_acronyms)]
16pub enum Error {
17    /// Interface not found.
18    InterfaceNotFound,
19    /// Invalid D-Bus address.
20    Address(String),
21    /// An I/O error.
22    InputOutput(Arc<io::Error>),
23    /// Invalid message field.
24    InvalidField,
25    /// Data too large.
26    ExcessData,
27    /// A [zvariant](https://docs.rs/zvariant) error.
28    Variant(VariantError),
29    /// A [zbus_names](https://docs.rs/zbus_names) error.
30    Names(NamesError),
31    /// Endian signature invalid or doesn't match expectation.
32    IncorrectEndian,
33    /// Initial handshake error.
34    Handshake(String),
35    /// Unexpected or incorrect reply.
36    InvalidReply,
37    /// A D-Bus method error reply.
38    // According to the spec, there can be all kinds of details in D-Bus errors but nobody adds
39    // anything more than a string description.
40    MethodError(OwnedErrorName, Option<String>, Message),
41    /// A required field is missing in the message headers.
42    MissingField,
43    /// Invalid D-Bus GUID.
44    InvalidGUID,
45    /// Unsupported function, or support currently lacking.
46    Unsupported,
47    /// A [`fdo::Error`] transformed into [`Error`].
48    FDO(Box<fdo::Error>),
49    /// The requested name was already claimed by another peer.
50    NameTaken,
51    /// Invalid [match rule][MR] string.
52    ///
53    /// [MR]: https://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules
54    InvalidMatchRule,
55    /// Generic error.
56    Failure(String),
57    /// A required parameter was missing.
58    MissingParameter(&'static str),
59    /// Serial number in the message header is 0 (which is invalid).
60    InvalidSerial,
61    /// The given interface already exists at the given path.
62    InterfaceExists(InterfaceName<'static>, ObjectPath<'static>),
63    /// Failed to connect to the D-Bus server at the given address.
64    Connection(Arc<io::Error>, Address),
65}
66
67impl PartialEq for Error {
68    fn eq(&self, other: &Self) -> bool {
69        match (self, other) {
70            (Self::Address(_), Self::Address(_)) => true,
71            (Self::InterfaceNotFound, Self::InterfaceNotFound) => true,
72            (Self::Handshake(_), Self::Handshake(_)) => true,
73            (Self::InvalidReply, Self::InvalidReply) => true,
74            (Self::ExcessData, Self::ExcessData) => true,
75            (Self::IncorrectEndian, Self::IncorrectEndian) => true,
76            (Self::MethodError(_, _, _), Self::MethodError(_, _, _)) => true,
77            (Self::MissingField, Self::MissingField) => true,
78            (Self::InvalidGUID, Self::InvalidGUID) => true,
79            (Self::InvalidSerial, Self::InvalidSerial) => true,
80            (Self::Unsupported, Self::Unsupported) => true,
81            (Self::FDO(s), Self::FDO(o)) => s == o,
82            (Self::InvalidField, Self::InvalidField) => true,
83            (Self::InvalidMatchRule, Self::InvalidMatchRule) => true,
84            (Self::Variant(s), Self::Variant(o)) => s == o,
85            (Self::Names(s), Self::Names(o)) => s == o,
86            (Self::NameTaken, Self::NameTaken) => true,
87            (Error::InputOutput(_), Self::InputOutput(_)) => false,
88            (Self::Failure(s1), Self::Failure(s2)) => s1 == s2,
89            (Self::InterfaceExists(s1, s2), Self::InterfaceExists(o1, o2)) => s1 == o1 && s2 == o2,
90            (Self::Connection(_, a1), Self::Connection(_, a2)) => a1 == a2,
91            (_, _) => false,
92        }
93    }
94}
95
96impl error::Error for Error {
97    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
98        match self {
99            Error::InterfaceNotFound => None,
100            Error::Address(_) => None,
101            Error::InputOutput(e) => Some(e),
102            Error::ExcessData => None,
103            Error::Handshake(_) => None,
104            Error::IncorrectEndian => None,
105            Error::Variant(e) => Some(e),
106            Error::Names(e) => Some(e),
107            Error::InvalidReply => None,
108            Error::MethodError(_, _, _) => None,
109            Error::InvalidGUID => None,
110            Error::Unsupported => None,
111            Error::FDO(e) => Some(e),
112            Error::InvalidField => None,
113            Error::MissingField => None,
114            Error::NameTaken => None,
115            Error::InvalidMatchRule => None,
116            Error::Failure(_) => None,
117            Error::MissingParameter(_) => None,
118            Error::InvalidSerial => None,
119            Error::InterfaceExists(_, _) => None,
120            Error::Connection(e, _) => Some(e),
121        }
122    }
123}
124
125impl fmt::Display for Error {
126    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127        match self {
128            Error::InterfaceNotFound => write!(f, "Interface not found"),
129            Error::Address(e) => write!(f, "address error: {e}"),
130            Error::ExcessData => write!(f, "excess data"),
131            Error::InputOutput(e) => write!(f, "I/O error: {e}"),
132            Error::Handshake(e) => write!(f, "D-Bus handshake failed: {e}"),
133            Error::IncorrectEndian => write!(f, "incorrect endian"),
134            Error::InvalidField => write!(f, "invalid message field"),
135            Error::Variant(e) => write!(f, "{e}"),
136            Error::Names(e) => write!(f, "{e}"),
137            Error::InvalidReply => write!(f, "Invalid D-Bus method reply"),
138            Error::MissingField => write!(f, "A required field is missing from message headers"),
139            Error::MethodError(name, detail, _reply) => write!(
140                f,
141                "{}: {}",
142                **name,
143                detail.as_ref().map(|s| s.as_str()).unwrap_or("no details")
144            ),
145            Error::InvalidGUID => write!(f, "Invalid GUID"),
146            Error::Unsupported => write!(f, "Connection support is lacking"),
147            Error::FDO(e) => write!(f, "{e}"),
148            Error::NameTaken => write!(f, "name already taken on the bus"),
149            Error::InvalidMatchRule => write!(f, "Invalid match rule string"),
150            Error::Failure(e) => write!(f, "{e}"),
151            Error::MissingParameter(p) => {
152                write!(f, "Parameter `{p}` was not specified but it is required")
153            }
154            Error::InvalidSerial => write!(f, "Serial number in the message header is 0"),
155            Error::InterfaceExists(i, p) => write!(f, "Interface `{i}` already exists at `{p}`"),
156            Error::Connection(e, addr) => write!(f, "Failed to connect to address `{addr}`: {e}"),
157        }
158    }
159}
160
161impl Error {
162    /// A description of the error.
163    ///
164    /// This is a generic description of the error (if any). For a more detailed description
165    /// make use of the [`std::fmt::Display`] implementation, for example, through
166    /// [`std::string::ToString`].
167    pub fn description(&self) -> Option<&str> {
168        match self {
169            Error::InterfaceNotFound => Some("interface not found"),
170            Error::Address(e) => Some(e),
171            Error::ExcessData => Some("excess data"),
172            Error::InputOutput(_) => Some("i/o error"),
173            Error::Handshake(e) => Some(e),
174            Error::IncorrectEndian => Some("incorrect endian"),
175            Error::InvalidField => Some("invalid field"),
176            Error::Variant(_) => Some("variant error"),
177            Error::Names(_) => Some("names error"),
178            Error::InvalidReply => Some("invalid reply"),
179            Error::MissingField => Some("a required field is missing from message headers"),
180            Error::MethodError(_, desc, _) => desc.as_deref(),
181            Error::InvalidGUID => Some("invalid GUID"),
182            Error::Unsupported => Some("connection support is lacking"),
183            Error::FDO(_) => Some("FDO error"),
184            Error::NameTaken => Some("name already taken on the bus"),
185            Error::InvalidMatchRule => Some("invalid match rule string"),
186            Error::Failure(e) => Some(e),
187            Error::MissingParameter(_) => Some("A required parameter is missing"),
188            Error::InvalidSerial => Some("serial number in the message header is 0"),
189            Error::InterfaceExists(_, _) => Some("interface already exists"),
190            Error::Connection(_, _) => Some("could not connect to specified address"),
191        }
192    }
193}
194
195impl Clone for Error {
196    fn clone(&self) -> Self {
197        match self {
198            Error::InterfaceNotFound => Error::InterfaceNotFound,
199            Error::Address(e) => Error::Address(e.clone()),
200            Error::ExcessData => Error::ExcessData,
201            Error::InputOutput(e) => Error::InputOutput(e.clone()),
202            Error::Handshake(e) => Error::Handshake(e.clone()),
203            Error::IncorrectEndian => Error::IncorrectEndian,
204            Error::InvalidField => Error::InvalidField,
205            Error::Variant(e) => Error::Variant(e.clone()),
206            Error::Names(e) => Error::Names(e.clone()),
207            Error::InvalidReply => Error::InvalidReply,
208            Error::MissingField => Error::MissingField,
209            Error::MethodError(name, detail, reply) => {
210                Error::MethodError(name.clone(), detail.clone(), reply.clone())
211            }
212            Error::InvalidGUID => Error::InvalidGUID,
213            Error::Unsupported => Error::Unsupported,
214            Error::FDO(e) => Error::FDO(e.clone()),
215            Error::NameTaken => Error::NameTaken,
216            Error::InvalidMatchRule => Error::InvalidMatchRule,
217            Error::Failure(e) => Error::Failure(e.clone()),
218            Error::MissingParameter(p) => Error::MissingParameter(p),
219            Error::InvalidSerial => Error::InvalidSerial,
220            Error::InterfaceExists(i, p) => Error::InterfaceExists(i.clone(), p.clone()),
221            Error::Connection(e, addr) => Error::Connection(e.clone(), addr.clone()),
222        }
223    }
224}
225
226impl From<io::Error> for Error {
227    fn from(val: io::Error) -> Self {
228        Error::InputOutput(Arc::new(val))
229    }
230}
231
232impl From<VariantError> for Error {
233    fn from(val: VariantError) -> Self {
234        Error::Variant(val)
235    }
236}
237
238impl From<zvariant::signature::Error> for Error {
239    fn from(e: zvariant::signature::Error) -> Self {
240        zvariant::Error::from(e).into()
241    }
242}
243
244impl From<NamesError> for Error {
245    fn from(val: NamesError) -> Self {
246        match val {
247            NamesError::Variant(e) => Error::Variant(e),
248            e => Error::Names(e),
249        }
250    }
251}
252
253impl From<fdo::Error> for Error {
254    fn from(val: fdo::Error) -> Self {
255        match val {
256            fdo::Error::ZBus(e) => e,
257            e => Error::FDO(Box::new(e)),
258        }
259    }
260}
261
262impl From<Infallible> for Error {
263    fn from(i: Infallible) -> Self {
264        match i {}
265    }
266}
267
268// For messages that are D-Bus error returns
269impl From<Message> for Error {
270    fn from(message: Message) -> Error {
271        // FIXME: Instead of checking this, we should have Method as trait and specific types for
272        // each message type.
273        let header = message.header();
274        if header.primary().msg_type() != Type::Error {
275            return Error::InvalidReply;
276        }
277
278        if let Some(name) = header.error_name() {
279            let name = name.to_owned().into();
280            match message.body().deserialize_unchecked::<&str>() {
281                Ok(detail) => Error::MethodError(name, Some(String::from(detail)), message),
282                Err(_) => Error::MethodError(name, None, message),
283            }
284        } else {
285            Error::InvalidReply
286        }
287    }
288}
289
290/// Alias for a `Result` with the error type `zbus::Error`.
291pub type Result<T> = std::result::Result<T, Error>;