auto_discovery/
error.rs

1//! Error types for the auto-discovery library
2
3use std::{
4    error::Error as StdError,
5    fmt,
6    io,
7    num::ParseIntError,
8    time::SystemTimeError,
9};
10use base64::DecodeError;
11#[cfg(feature = "secure")]
12use ring::error::{KeyRejected, Unspecified};
13
14/// The primary error type for the auto-discovery crate
15#[derive(Debug)]
16pub enum DiscoveryError {
17    /// Invalid configuration error
18    Configuration(String),
19    /// Invalid service data error
20    InvalidData(String),
21    /// Invalid service info error
22    InvalidServiceInfo { 
23        /// The field that contains invalid data
24        field: String, 
25        /// The reason why the field is invalid
26        reason: String 
27    },
28    /// Service not found error
29    ServiceNotFound(String),
30    /// DNS resolution error
31    DnsResolution(String),
32    /// mDNS protocol error
33    Mdns(String),
34    /// UPnP/SSDP protocol error
35    Upnp(String),
36    /// DNS-SD protocol error
37    DnsSd(String),
38    /// Network operation error
39    Network(String),
40    /// Protocol operation timeout
41    Timeout(String),
42    /// Service verification error
43    Verification(String),
44    /// Protocol error
45    Protocol(String),
46    /// I/O error
47    Io(io::Error),
48    /// Security error
49    Security(String),
50    /// Other error types
51    Other(String),
52}
53
54impl fmt::Display for DiscoveryError {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        match self {
57            Self::Configuration(msg) => write!(f, "Configuration error: {msg}"),
58            Self::InvalidData(msg) => write!(f, "Invalid data: {msg}"),
59            Self::InvalidServiceInfo { field, reason } => {
60                write!(f, "Invalid service info ({field}): {reason}")
61            }
62            Self::ServiceNotFound(msg) => write!(f, "Service not found: {msg}"),
63            Self::DnsResolution(msg) => write!(f, "DNS resolution error: {msg}"),
64            Self::Mdns(msg) => write!(f, "mDNS error: {msg}"),
65            Self::Upnp(msg) => write!(f, "UPnP error: {msg}"),
66            Self::DnsSd(msg) => write!(f, "DNS-SD error: {msg}"),
67            Self::Network(msg) => write!(f, "Network error: {msg}"),
68            Self::Timeout(msg) => write!(f, "Timeout: {msg}"),
69            Self::Verification(msg) => write!(f, "Verification error: {msg}"),
70            Self::Protocol(msg) => write!(f, "Protocol error: {msg}"),
71            Self::Io(err) => write!(f, "I/O error: {err}"),
72            Self::Security(msg) => write!(f, "Security error: {msg}"),
73            Self::Other(msg) => write!(f, "Error: {msg}"),
74        }
75    }
76}
77
78impl StdError for DiscoveryError {
79    fn source(&self) -> Option<&(dyn StdError + 'static)> {
80        match self {
81            Self::Io(err) => Some(err),
82            _ => None,
83        }
84    }
85}
86
87impl From<io::Error> for DiscoveryError {
88    fn from(err: io::Error) -> Self {
89        Self::Io(err)
90    }
91}
92
93impl From<ParseIntError> for DiscoveryError {
94    fn from(err: ParseIntError) -> Self {
95        Self::InvalidData(err.to_string())
96    }
97}
98
99impl From<SystemTimeError> for DiscoveryError {
100    fn from(err: SystemTimeError) -> Self {
101        Self::Other(err.to_string())
102    }
103}
104
105impl From<DecodeError> for DiscoveryError {
106    fn from(err: DecodeError) -> Self {
107        Self::Security(err.to_string())
108    }
109}
110
111#[cfg(feature = "secure")]
112impl From<Unspecified> for DiscoveryError {
113    fn from(err: Unspecified) -> Self {
114        Self::Security(err.to_string())
115    }
116}
117
118#[cfg(feature = "secure")]
119impl From<KeyRejected> for DiscoveryError {
120    fn from(err: KeyRejected) -> Self {
121        Self::Security(err.to_string())
122    }
123}
124
125impl From<mdns_sd::Error> for DiscoveryError {
126    fn from(err: mdns_sd::Error) -> Self {
127        Self::Mdns(err.to_string())
128    }
129}
130
131/// Error severity levels
132#[derive(Debug, Clone, Copy, PartialEq, Eq)]
133pub enum ErrorSeverity {
134    /// Fatal error requiring immediate attention
135    Fatal,
136    /// Error condition
137    Error,
138    /// Warning condition
139    Warning,
140    /// Informational message
141    Info,
142}
143
144/// Common result type for library operations
145pub type Result<T> = std::result::Result<T, DiscoveryError>;
146
147impl DiscoveryError {
148    /// Create a new configuration error
149    pub fn configuration<S: Into<String>>(msg: S) -> Self {
150        Self::Configuration(msg.into())
151    }
152
153    /// Create a new invalid data error
154    pub fn invalid_data<S: Into<String>>(msg: S) -> Self {
155        Self::InvalidData(msg.into())
156    }
157
158    /// Create a new service not found error
159    pub fn service_not_found<S: Into<String>>(msg: S) -> Self {
160        Self::ServiceNotFound(msg.into())
161    }
162
163    /// Create a new DNS resolution error
164    pub fn dns_resolution<S: Into<String>>(msg: S) -> Self {
165        Self::DnsResolution(msg.into())
166    }
167
168    /// Create a new mDNS protocol error
169    pub fn mdns<S: Into<String>>(msg: S) -> Self {
170        Self::Mdns(msg.into())
171    }
172
173    /// Create a new UPnP protocol error
174    pub fn upnp<S: Into<String>>(msg: S) -> Self {
175        Self::Upnp(msg.into())
176    }
177
178    /// Create a new DNS-SD protocol error
179    pub fn dns_sd<S: Into<String>>(msg: S) -> Self {
180        Self::DnsSd(msg.into())
181    }
182
183    /// Create a new network error
184    pub fn network<S: Into<String>>(msg: S) -> Self {
185        Self::Network(msg.into())
186    }
187
188    /// Create a new timeout error
189    pub fn timeout<S: Into<String>>(msg: S) -> Self {
190        Self::Timeout(msg.into())
191    }
192
193    /// Create a new verification error
194    pub fn verification<S: Into<String>>(msg: S) -> Self {
195        Self::Verification(msg.into())
196    }
197
198    /// Create a new protocol error
199    pub fn protocol<S: Into<String>>(msg: S) -> Self {
200        Self::Protocol(msg.into())
201    }
202
203    /// Create a new security error
204    pub fn security<S: Into<String>>(msg: S) -> Self {
205        Self::Security(msg.into())
206    }
207
208    /// Create a new other error
209    pub fn other<S: Into<String>>(msg: S) -> Self {
210        Self::Other(msg.into())
211    }
212
213    /// Create a new invalid service error
214    pub fn invalid_service<S: Into<String>>(msg: S) -> Self {
215        Self::InvalidData(msg.into())
216    }
217
218    /// Check if error is retryable
219    pub fn is_retryable(&self) -> bool {
220        matches!(
221            self,
222            Self::Network(_) | Self::Timeout(_) | Self::Protocol(_)
223        )
224    }
225
226    /// Get error severity
227    pub fn severity(&self) -> ErrorSeverity {
228        match self {
229            Self::Configuration(_) | Self::InvalidData(_) => ErrorSeverity::Fatal,
230            Self::Security(_) | Self::Verification(_) => ErrorSeverity::Error,
231            Self::Network(_) | Self::DnsResolution(_) | Self::Protocol(_) => ErrorSeverity::Warning,
232            Self::Timeout(_) => ErrorSeverity::Info,
233            _ => ErrorSeverity::Warning,
234        }
235    }
236}
237
238// Test module
239#[cfg(test)]
240mod tests {
241    use super::*;
242
243    #[test]
244    fn test_error_severity() {
245        assert_eq!(
246            DiscoveryError::DnsResolution("test".to_string()).severity(),
247            ErrorSeverity::Warning
248        );
249        assert_eq!(
250            DiscoveryError::protocol("test".to_string()).severity(),
251            ErrorSeverity::Warning
252        );
253    }
254
255    #[test]
256    fn test_error_retryable() {
257        assert!(DiscoveryError::Timeout("5".to_string()).is_retryable());
258        assert!(!DiscoveryError::invalid_service("test".to_string()).is_retryable());
259    }
260}