1 | #[cfg (feature = "quick-xml" )] |
2 | use quick_xml::de::DeError; |
3 | use static_assertions::assert_impl_all; |
4 | use std::{convert::Infallible, error, fmt, io, sync::Arc}; |
5 | use zbus_names::{Error as NamesError, OwnedErrorName}; |
6 | use zvariant::Error as VariantError; |
7 | |
8 | use 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)] |
16 | pub 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 | |
71 | assert_impl_all!(Error: Send, Sync, Unpin); |
72 | |
73 | impl 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 | |
106 | impl 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 | |
139 | impl 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 | |
179 | impl 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 | |
215 | impl From<io::Error> for Error { |
216 | fn from(val: io::Error) -> Self { |
217 | Error::InputOutput(Arc::new(data:val)) |
218 | } |
219 | } |
220 | |
221 | #[cfg (unix)] |
222 | impl From<nix::Error> for Error { |
223 | fn from(val: nix::Error) -> Self { |
224 | io::Error::from_raw_os_error(code:val as i32).into() |
225 | } |
226 | } |
227 | |
228 | impl From<VariantError> for Error { |
229 | fn from(val: VariantError) -> Self { |
230 | Error::Variant(val) |
231 | } |
232 | } |
233 | |
234 | impl From<NamesError> for Error { |
235 | fn from(val: NamesError) -> Self { |
236 | match val { |
237 | NamesError::Variant(e: Error) => Error::Variant(e), |
238 | e: Error => Error::Names(e), |
239 | } |
240 | } |
241 | } |
242 | |
243 | impl From<fdo::Error> for Error { |
244 | fn from(val: fdo::Error) -> Self { |
245 | match val { |
246 | fdo::Error::ZBus(e: Error) => e, |
247 | e: Error => Error::FDO(Box::new(e)), |
248 | } |
249 | } |
250 | } |
251 | |
252 | #[cfg (feature = "xml" )] |
253 | impl 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" )] |
260 | impl From<DeError> for Error { |
261 | fn from(val: DeError) -> Self { |
262 | Error::QuickXml(val) |
263 | } |
264 | } |
265 | |
266 | impl 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 |
273 | impl From<Message> for Error { |
274 | fn from(message: Message) -> Error { |
275 | Self::from(Arc::new(data:message)) |
276 | } |
277 | } |
278 | |
279 | impl 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`. |
306 | pub type Result<T> = std::result::Result<T, Error>; |
307 | |