| 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 | |