zbus/
error.rs

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