Skip to main content

tokio_dbus_runtime/
error.rs

1use std::error;
2use std::fmt;
3use std::time::Duration;
4
5use tokio_dbus::org_freedesktop_dbus;
6use tokio_dbus::{SignatureBuf, SignatureError};
7
8/// Result alias defaulting to the error type of this crate.
9pub type Result<T, E = Error> = std::result::Result<T, E>;
10
11/// An error raised while talking to the bus.
12#[derive(Debug)]
13pub struct Error {
14    // NB: Boxed so that a `Result` carrying this error stays small, since every
15    // encode and decode threads one through.
16    kind: Box<ErrorKind>,
17}
18
19impl Error {
20    pub(crate) fn new(kind: ErrorKind) -> Self {
21        Self {
22            kind: Box::new(kind),
23        }
24    }
25
26    /// Construct an error which is reported back to a caller as an error reply.
27    ///
28    /// # Examples
29    ///
30    /// ```
31    /// use tokio_dbus_runtime::Error;
32    ///
33    /// let error = Error::remote("com.example.Error.Busy", "Try again later");
34    /// assert_eq!(error.name(), Some("com.example.Error.Busy"));
35    /// ```
36    pub fn remote(name: impl AsRef<str>, message: impl fmt::Display) -> Self {
37        Self::new(ErrorKind::Remote {
38            name: name.as_ref().into(),
39            message: message.to_string().into(),
40        })
41    }
42
43    /// The D-Bus error name, if this error came from, or is destined for, an
44    /// error reply.
45    ///
46    /// A timeout raised by this end reports itself as
47    /// `org.freedesktop.DBus.Error.NoReply`, which is the name every
48    /// implementation uses for a call which was not answered.
49    pub fn name(&self) -> Option<&str> {
50        match &*self.kind {
51            ErrorKind::Remote { name, .. } => Some(name),
52            ErrorKind::Timeout(..) => Some(org_freedesktop_dbus::NO_REPLY_ERROR),
53            _ => None,
54        }
55    }
56
57    /// Test if this error is an error reply from the remote end.
58    ///
59    /// When this is `false` the error was raised locally, such as a transport
60    /// failure or a timeout, and retrying against the same peer is unlikely to
61    /// behave differently.
62    ///
63    /// # Examples
64    ///
65    /// ```
66    /// use tokio_dbus_runtime::Error;
67    ///
68    /// let error = Error::remote("com.example.Error.Busy", "Try again later");
69    /// assert!(error.is_remote());
70    /// ```
71    pub fn is_remote(&self) -> bool {
72        matches!(&*self.kind, ErrorKind::Remote { .. })
73    }
74
75    /// Test if this error was raised by [`Connection::acquire_name`] because
76    /// the name was already taken.
77    ///
78    /// [`Connection::acquire_name`]: crate::Connection::acquire_name
79    pub fn is_name_taken(&self) -> bool {
80        matches!(&*self.kind, ErrorKind::NameTaken(..))
81    }
82
83    /// Test if this error is a call which timed out.
84    ///
85    /// See [`Connection::set_default_timeout`].
86    ///
87    /// [`Connection::set_default_timeout`]: crate::Connection::set_default_timeout
88    pub fn is_timeout(&self) -> bool {
89        matches!(&*self.kind, ErrorKind::Timeout(..))
90    }
91}
92
93impl From<tokio_dbus::Error> for Error {
94    #[inline]
95    fn from(error: tokio_dbus::Error) -> Self {
96        Self::new(ErrorKind::Dbus(error))
97    }
98}
99
100impl From<SignatureError> for Error {
101    #[inline]
102    fn from(error: SignatureError) -> Self {
103        Self::new(ErrorKind::Signature(error))
104    }
105}
106
107impl From<std::io::Error> for Error {
108    #[inline]
109    fn from(error: std::io::Error) -> Self {
110        Self::new(ErrorKind::Dbus(error.into()))
111    }
112}
113
114impl fmt::Display for Error {
115    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116        match &*self.kind {
117            ErrorKind::Dbus(..) => write!(f, "D-Bus error"),
118            ErrorKind::Signature(..) => write!(f, "Signature error"),
119            ErrorKind::Remote { name, message } => write!(f, "{name}: {message}"),
120            ErrorKind::UnsupportedType(signature) => {
121                write!(f, "Cannot represent a value of type `{signature}`")
122            }
123            ErrorKind::UnexpectedSignature(signatures) => {
124                let (expected, actual) = &**signatures;
125                write!(f, "Expected a value of type `{expected}`, got `{actual}`")
126            }
127            ErrorKind::MissingUniqueName => {
128                write!(f, "The bus did not reply to `Hello` with a unique name")
129            }
130            ErrorKind::NameTaken(name) => {
131                write!(f, "Could not acquire the name `{name}`")
132            }
133            ErrorKind::Timeout(timeout) => {
134                write!(f, "Call did not receive a reply within {timeout:?}")
135            }
136        }
137    }
138}
139
140impl error::Error for Error {
141    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
142        match &*self.kind {
143            ErrorKind::Dbus(error) => Some(error),
144            ErrorKind::Signature(error) => Some(error),
145            _ => None,
146        }
147    }
148}
149
150#[derive(Debug)]
151pub(crate) enum ErrorKind {
152    Dbus(tokio_dbus::Error),
153    Signature(SignatureError),
154    Remote { name: Box<str>, message: Box<str> },
155    UnsupportedType(Box<SignatureBuf>),
156    // NB: Boxed because a `SignatureBuf` is an inline buffer, so a variant with
157    // two of them is much larger than any of the others.
158    UnexpectedSignature(Box<(SignatureBuf, SignatureBuf)>),
159    MissingUniqueName,
160    NameTaken(Box<str>),
161    Timeout(Duration),
162}