zbus/
error.rs

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