tosca_controller/
error.rs

1use std::borrow::Cow;
2
3use tracing::error;
4
5/// All possible error kinds.
6#[derive(Debug, Copy, Clone, PartialEq)]
7pub enum ErrorKind {
8    /// Errors encountered during the discovery service.
9    Discovery,
10    /// Errors encountered when sending requests to a device.
11    Request,
12    /// Errors caused by an invalid parameter.
13    InvalidParameter,
14    /// Errors encountered while parsing a `json` response.
15    JsonResponse,
16    /// Errors encountered while parsing a byte stream response.
17    #[cfg(feature = "stream")]
18    StreamResponse,
19    /// Errors encountered while constructing the request sender.
20    Sender,
21    /// Errors related to event management.
22    Events,
23}
24
25impl ErrorKind {
26    pub(crate) const fn description(self) -> &'static str {
27        match self {
28            Self::Discovery => "Discovery",
29            Self::Request => "Request",
30            Self::InvalidParameter => "Invalid Parameter",
31            Self::JsonResponse => "Json Response",
32            #[cfg(feature = "stream")]
33            Self::StreamResponse => "Stream Response",
34            Self::Sender => "Response Sender",
35            Self::Events => "Events",
36        }
37    }
38}
39
40impl std::fmt::Display for ErrorKind {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        self.description().fmt(f)
43    }
44}
45
46/// Controller error.
47#[derive(PartialEq)]
48pub struct Error {
49    kind: ErrorKind,
50    description: Cow<'static, str>,
51}
52
53impl std::fmt::Display for Error {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        self.format(f)
56    }
57}
58
59impl std::fmt::Debug for Error {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        self.format(f)
62    }
63}
64
65impl Error {
66    /// Creates an [`Error`] from an [`ErrorKind`] and a description.
67    #[inline]
68    pub fn new(kind: ErrorKind, description: impl Into<Cow<'static, str>>) -> Self {
69        let description = description.into();
70        error!("{}", description.as_ref());
71        Self { kind, description }
72    }
73
74    fn format(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75        write!(f, "{}: {}", self.kind, self.description)
76    }
77}
78
79impl From<reqwest::Error> for Error {
80    fn from(e: reqwest::Error) -> Self {
81        Self::new(ErrorKind::Request, e.to_string())
82    }
83}
84
85impl From<mdns_sd::Error> for Error {
86    fn from(e: mdns_sd::Error) -> Self {
87        Self::new(ErrorKind::Discovery, e.to_string())
88    }
89}
90
91impl From<rumqttc::v5::ClientError> for Error {
92    fn from(e: rumqttc::v5::ClientError) -> Self {
93        Self::new(ErrorKind::Events, e.to_string())
94    }
95}
96
97impl std::error::Error for Error {
98    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
99        Some(self)
100    }
101}
102
103/// A specialized [`Result`] type for [`Error`].
104pub type Result<T> = std::result::Result<T, Error>;
105
106#[cfg(test)]
107mod tests {
108    use super::{Error, ErrorKind};
109
110    #[test]
111    fn controller_error() {
112        let error = Error::new(ErrorKind::Discovery, "Process failed.");
113
114        assert_eq!(error.to_string(), r"Discovery: Process failed.");
115    }
116}