1 | use static_assertions::assert_impl_all; |
2 | use std::{convert::Infallible, error, fmt, io, sync::Arc}; |
3 | use zbus_names::{Error as NamesError, InterfaceName, OwnedErrorName}; |
4 | use zvariant::{Error as VariantError, ObjectPath}; |
5 | |
6 | use 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)] |
17 | pub 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 | |
66 | assert_impl_all!(Error: Send, Sync, Unpin); |
67 | |
68 | impl 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 | |
96 | impl 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 | |
124 | impl 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 | |
159 | impl 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 | |
189 | impl From<io::Error> for Error { |
190 | fn from(val: io::Error) -> Self { |
191 | Error::InputOutput(Arc::new(data:val)) |
192 | } |
193 | } |
194 | |
195 | #[cfg (unix)] |
196 | impl From<nix::Error> for Error { |
197 | fn from(val: nix::Error) -> Self { |
198 | io::Error::from_raw_os_error(code:val as i32).into() |
199 | } |
200 | } |
201 | |
202 | impl From<VariantError> for Error { |
203 | fn from(val: VariantError) -> Self { |
204 | Error::Variant(val) |
205 | } |
206 | } |
207 | |
208 | impl From<NamesError> for Error { |
209 | fn from(val: NamesError) -> Self { |
210 | match val { |
211 | NamesError::Variant(e: Error) => Error::Variant(e), |
212 | e: Error => Error::Names(e), |
213 | } |
214 | } |
215 | } |
216 | |
217 | impl From<fdo::Error> for Error { |
218 | fn from(val: fdo::Error) -> Self { |
219 | match val { |
220 | fdo::Error::ZBus(e: Error) => e, |
221 | e: Error => Error::FDO(Box::new(e)), |
222 | } |
223 | } |
224 | } |
225 | |
226 | impl 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 |
233 | impl 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: Header<'_> = message.header(); |
238 | if header.primary().msg_type() != Type::Error { |
239 | return Error::InvalidReply; |
240 | } |
241 | |
242 | if let Some(name: &ErrorName<'_>) = header.error_name() { |
243 | let name: OwnedErrorName = name.to_owned().into(); |
244 | match message.body().deserialize_unchecked::<&str>() { |
245 | Ok(detail: &str) => 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`. |
255 | pub type Result<T> = std::result::Result<T, Error>; |
256 | |