use thiserror in tokio-xmpp and minidom

lemonsh created

Change summary

minidom/Cargo.toml      |   1 
minidom/src/error.rs    |  46 ++--------
tokio-xmpp/Cargo.toml   |   1 
tokio-xmpp/src/error.rs | 191 +++++++++---------------------------------
4 files changed, 53 insertions(+), 186 deletions(-)

Detailed changes

minidom/Cargo.toml 🔗

@@ -22,6 +22,7 @@ gitlab = { repository = "xmpp-rs/xmpp-rs" }
 
 [dependencies]
 rxml = { version = "0.13.1", default-features = false, features = ["compact_str"] }
+thiserror = "2.0"
 
 [features]
 default = [ "std" ]

minidom/src/error.rs 🔗

@@ -13,13 +13,14 @@
 
 use std::io;
 
-use core::{error::Error as StdError, fmt};
+use thiserror::Error;
 
 /// Our main error type.
-#[derive(Debug)]
+#[derive(Debug, Error)]
 pub enum Error {
     /// Error from rxml parsing or writing
-    XmlError(rxml::Error),
+    #[error("XML error: {0}")]
+    XmlError(#[from] rxml::Error),
 
     /// I/O error from accessing the source or destination.
     ///
@@ -27,35 +28,27 @@ pub enum Error {
     /// [`io::Error`] when using it with [`BufRead`][`io::BufRead`],
     /// any rxml errors will still be reported through the
     /// [`XmlError`][`Self::XmlError`] variant.
+    #[error("I/O error: {0}")]
     Io(io::Error),
 
     /// An error which is returned when the end of the document was reached prematurely.
+    #[error("the end of the document has been reached prematurely")]
     EndOfDocument,
 
     /// An error which is returned when an element being serialized doesn't contain a prefix
     /// (be it None or Some(_)).
+    #[error("the prefix is invalid")]
     InvalidPrefix,
 
     /// An error which is returned when an element doesn't contain a namespace
+    #[error("the XML element is missing a namespace")]
     MissingNamespace,
 
     /// An error which is returned when a prefixed is defined twice
+    #[error("the prefix is already defined")]
     DuplicatePrefix,
 }
 
-impl StdError for Error {
-    fn cause(&self) -> Option<&dyn StdError> {
-        match self {
-            Error::XmlError(e) => Some(e),
-            Error::Io(e) => Some(e),
-            Error::EndOfDocument
-            | Error::InvalidPrefix
-            | Error::MissingNamespace
-            | Error::DuplicatePrefix => None,
-        }
-    }
-}
-
 impl From<io::Error> for Error {
     fn from(other: io::Error) -> Self {
         match other.downcast::<rxml::Error>() {
@@ -65,27 +58,6 @@ impl From<io::Error> for Error {
     }
 }
 
-impl fmt::Display for Error {
-    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        match self {
-            Error::XmlError(e) => write!(fmt, "XML error: {e}"),
-            Error::Io(e) => write!(fmt, "I/O error: {e}"),
-            Error::EndOfDocument => {
-                write!(fmt, "the end of the document has been reached prematurely")
-            }
-            Error::InvalidPrefix => write!(fmt, "the prefix is invalid"),
-            Error::MissingNamespace => write!(fmt, "the XML element is missing a namespace",),
-            Error::DuplicatePrefix => write!(fmt, "the prefix is already defined"),
-        }
-    }
-}
-
-impl From<rxml::Error> for Error {
-    fn from(err: rxml::Error) -> Error {
-        Error::XmlError(err)
-    }
-}
-
 impl From<rxml::strings::Error> for Error {
     fn from(err: rxml::strings::Error) -> Error {
         rxml::error::Error::from(err).into()

tokio-xmpp/Cargo.toml 🔗

@@ -23,6 +23,7 @@ rxml = { version = "0.13.1", features = ["compact_str"] }
 rand = "0.9"
 syntect = { version = "5", optional = true }
 pin-project-lite = { version = "0.2" }
+thiserror = "2.0"
 # same repository dependencies
 sasl = { version = "0.5", path = "../sasl" }
 xmpp-parsers = { version = "0.21", path = "../parsers", features = [ "log" ] }

tokio-xmpp/src/error.rs 🔗

@@ -1,8 +1,9 @@
-use core::{error::Error as StdError, fmt, net::AddrParseError, str::Utf8Error};
+use core::{fmt, net::AddrParseError, str::Utf8Error};
 #[cfg(feature = "dns")]
 use hickory_resolver::{proto::ProtoError as DnsProtoError, ResolveError as DnsResolveError};
 use sasl::client::MechanismError as SaslMechanismError;
 use std::io;
+use thiserror::Error;
 
 use xmpp_parsers::stream_error::ReceivedStreamError;
 
@@ -12,110 +13,62 @@ use crate::{
 };
 
 /// Top-level error type
-#[derive(Debug)]
+#[derive(Debug, Error)]
 pub enum Error {
     /// I/O error
-    Io(io::Error),
+    #[error("I/O error: {0}")]
+    Io(#[from] io::Error),
     /// Error parsing Jabber-Id
-    JidParse(jid::Error),
+    #[error("JID parse error: {0}")]
+    JidParse(#[from] jid::Error),
     /// Protocol-level error
-    Protocol(ProtocolError),
+    #[error("protocol error: {0}")]
+    Protocol(#[from] ProtocolError),
     /// Authentication error
-    Auth(AuthError),
+    #[error("authentication error: {0}")]
+    Auth(#[from] AuthError),
     /// Connection closed
+    #[error("disconnected")]
     Disconnected,
     /// Should never happen
+    #[error("invalid state")]
     InvalidState,
     /// Fmt error
-    Fmt(fmt::Error),
+    #[error("fmt error: {0}")]
+    Fmt(#[from] fmt::Error),
     /// Utf8 error
-    Utf8(Utf8Error),
+    #[error("UTF-8 error: {0}")]
+    Utf8(#[from] Utf8Error),
     /// Error specific to ServerConnector impl
+    #[error("connection error: {0}")]
     Connection(Box<dyn ServerConnectorError>),
     /// DNS protocol error
     #[cfg(feature = "dns")]
-    Dns(DnsProtoError),
+    #[error("{0:?}")]
+    Dns(#[from] DnsProtoError),
     /// DNS resolution error
     #[cfg(feature = "dns")]
-    Resolve(DnsResolveError),
+    #[error("{0:?}")]
+    Resolve(#[from] DnsResolveError),
     /// DNS label conversion error, no details available from module
     /// `idna`
     #[cfg(feature = "dns")]
+    #[error("IDNA error")]
     Idna,
     /// Invalid IP/Port address
-    Addr(AddrParseError),
+    #[error("wrong network address: {0}")]
+    Addr(#[from] AddrParseError),
     /// Received a stream error
+    #[error("{0}")]
     StreamError(ReceivedStreamError),
 }
 
-impl fmt::Display for Error {
-    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        match self {
-            Error::Io(e) => write!(fmt, "IO error: {}", e),
-            Error::Connection(e) => write!(fmt, "connection error: {}", e),
-            Error::JidParse(e) => write!(fmt, "jid parse error: {}", e),
-            Error::Protocol(e) => write!(fmt, "protocol error: {}", e),
-            Error::Auth(e) => write!(fmt, "authentication error: {}", e),
-            Error::Disconnected => write!(fmt, "disconnected"),
-            Error::InvalidState => write!(fmt, "invalid state"),
-            Error::Fmt(e) => write!(fmt, "Fmt error: {}", e),
-            Error::Utf8(e) => write!(fmt, "Utf8 error: {}", e),
-            #[cfg(feature = "dns")]
-            Error::Dns(e) => write!(fmt, "{:?}", e),
-            #[cfg(feature = "dns")]
-            Error::Resolve(e) => write!(fmt, "{:?}", e),
-            #[cfg(feature = "dns")]
-            Error::Idna => write!(fmt, "IDNA error"),
-            Error::Addr(e) => write!(fmt, "Wrong network address: {e}"),
-            Error::StreamError(e) => write!(fmt, "{e}"),
-        }
-    }
-}
-
-impl StdError for Error {}
-
-impl From<io::Error> for Error {
-    fn from(e: io::Error) -> Self {
-        Error::Io(e)
-    }
-}
-
 impl<T: ServerConnectorError + 'static> From<T> for Error {
     fn from(e: T) -> Self {
         Error::Connection(Box::new(e))
     }
 }
 
-impl From<jid::Error> for Error {
-    fn from(e: jid::Error) -> Self {
-        Error::JidParse(e)
-    }
-}
-
-impl From<ProtocolError> for Error {
-    fn from(e: ProtocolError) -> Self {
-        Error::Protocol(e)
-    }
-}
-
-impl From<AuthError> for Error {
-    fn from(e: AuthError) -> Self {
-        Error::Auth(e)
-    }
-}
-
-impl From<fmt::Error> for Error {
-    fn from(e: fmt::Error) -> Self {
-        Error::Fmt(e)
-    }
-}
-
-impl From<Utf8Error> for Error {
-    fn from(e: Utf8Error) -> Self {
-        Error::Utf8(e)
-    }
-}
-
 #[cfg(feature = "dns")]
 impl From<idna::Errors> for Error {
     fn from(_e: idna::Errors) -> Self {
@@ -123,26 +76,6 @@ impl From<idna::Errors> for Error {
     }
 }
 
-#[cfg(feature = "dns")]
-impl From<DnsResolveError> for Error {
-    fn from(e: DnsResolveError) -> Error {
-        Error::Resolve(e)
-    }
-}
-
-#[cfg(feature = "dns")]
-impl From<DnsProtoError> for Error {
-    fn from(e: DnsProtoError) -> Error {
-        Error::Dns(e)
-    }
-}
-
-impl From<AddrParseError> for Error {
-    fn from(e: AddrParseError) -> Error {
-        Error::Addr(e)
-    }
-}
-
 impl From<RecvFeaturesError> for Error {
     fn from(e: RecvFeaturesError) -> Self {
         match e {
@@ -153,87 +86,47 @@ impl From<RecvFeaturesError> for Error {
 }
 
 /// XMPP protocol-level error
-#[derive(Debug)]
+#[derive(Debug, Error)]
 pub enum ProtocolError {
     /// XML parser error
-    Parser(minidom::Error),
+    #[error("XML parser error: {0}")]
+    Parser(#[from] minidom::Error),
     /// Error with expected stanza schema
-    Parsers(xso::error::Error),
+    #[error("error with expected stanza schema: {0}")]
+    Parsers(#[from] xso::error::Error),
     /// No TLS available
+    #[error("no TLS available")]
     NoTls,
     /// Invalid response to resource binding
+    #[error("invalid response to resource binding")]
     InvalidBindResponse,
     /// No xmlns attribute in <stream:stream>
+    #[error("no xmlns attribute in <stream:stream>")]
     NoStreamNamespace,
     /// No id attribute in <stream:stream>
+    #[error("no id attribute in <stream:stream>")]
     NoStreamId,
     /// Encountered an unexpected XML token
+    #[error("encountered an unexpected XML token")]
     InvalidToken,
     /// Unexpected <stream:stream> (shouldn't occur)
+    #[error("unexpected <stream:stream>")]
     InvalidStreamStart,
 }
 
-impl fmt::Display for ProtocolError {
-    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        match self {
-            ProtocolError::Parser(e) => write!(fmt, "XML parser error: {}", e),
-            ProtocolError::Parsers(e) => write!(fmt, "error with expected stanza schema: {}", e),
-            ProtocolError::NoTls => write!(fmt, "no TLS available"),
-            ProtocolError::InvalidBindResponse => {
-                write!(fmt, "invalid response to resource binding")
-            }
-            ProtocolError::NoStreamNamespace => {
-                write!(fmt, "no xmlns attribute in <stream:stream>")
-            }
-            ProtocolError::NoStreamId => write!(fmt, "no id attribute in <stream:stream>"),
-            ProtocolError::InvalidToken => write!(fmt, "encountered an unexpected XML token"),
-            ProtocolError::InvalidStreamStart => write!(fmt, "unexpected <stream:stream>"),
-        }
-    }
-}
-
-impl StdError for ProtocolError {}
-
-impl From<minidom::Error> for ProtocolError {
-    fn from(e: minidom::Error) -> Self {
-        ProtocolError::Parser(e)
-    }
-}
-
-impl From<minidom::Error> for Error {
-    fn from(e: minidom::Error) -> Self {
-        ProtocolError::Parser(e).into()
-    }
-}
-
-impl From<xso::error::Error> for ProtocolError {
-    fn from(e: xso::error::Error) -> Self {
-        ProtocolError::Parsers(e)
-    }
-}
-
 /// Authentication error
-#[derive(Debug)]
+#[derive(Debug, Error)]
 pub enum AuthError {
     /// No matching SASL mechanism available
+    #[error("no matching SASL mechanism available")]
     NoMechanism,
     /// Local SASL implementation error
+    #[error("local SASL implementation error: {0}")]
     Sasl(SaslMechanismError),
     /// Failure from server
+    #[error("failure from the server: {0:?}")]
     Fail(SaslDefinedCondition),
     /// Component authentication failure
+    #[error("component authentication failure")]
     ComponentFail,
 }
-
-impl StdError for AuthError {}
-
-impl fmt::Display for AuthError {
-    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        match self {
-            AuthError::NoMechanism => write!(fmt, "no matching SASL mechanism available"),
-            AuthError::Sasl(s) => write!(fmt, "local SASL implementation error: {}", s),
-            AuthError::Fail(c) => write!(fmt, "failure from the server: {:?}", c),
-            AuthError::ComponentFail => write!(fmt, "component authentication failure"),
-        }
-    }
-}