1use 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#[derive(Debug)]
16pub enum DiscoveryError {
17 Configuration(String),
19 InvalidData(String),
21 InvalidServiceInfo {
23 field: String,
25 reason: String
27 },
28 ServiceNotFound(String),
30 DnsResolution(String),
32 Mdns(String),
34 Upnp(String),
36 DnsSd(String),
38 Network(String),
40 Timeout(String),
42 Verification(String),
44 Protocol(String),
46 Io(io::Error),
48 Security(String),
50 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
133pub enum ErrorSeverity {
134 Fatal,
136 Error,
138 Warning,
140 Info,
142}
143
144pub type Result<T> = std::result::Result<T, DiscoveryError>;
146
147impl DiscoveryError {
148 pub fn configuration<S: Into<String>>(msg: S) -> Self {
150 Self::Configuration(msg.into())
151 }
152
153 pub fn invalid_data<S: Into<String>>(msg: S) -> Self {
155 Self::InvalidData(msg.into())
156 }
157
158 pub fn service_not_found<S: Into<String>>(msg: S) -> Self {
160 Self::ServiceNotFound(msg.into())
161 }
162
163 pub fn dns_resolution<S: Into<String>>(msg: S) -> Self {
165 Self::DnsResolution(msg.into())
166 }
167
168 pub fn mdns<S: Into<String>>(msg: S) -> Self {
170 Self::Mdns(msg.into())
171 }
172
173 pub fn upnp<S: Into<String>>(msg: S) -> Self {
175 Self::Upnp(msg.into())
176 }
177
178 pub fn dns_sd<S: Into<String>>(msg: S) -> Self {
180 Self::DnsSd(msg.into())
181 }
182
183 pub fn network<S: Into<String>>(msg: S) -> Self {
185 Self::Network(msg.into())
186 }
187
188 pub fn timeout<S: Into<String>>(msg: S) -> Self {
190 Self::Timeout(msg.into())
191 }
192
193 pub fn verification<S: Into<String>>(msg: S) -> Self {
195 Self::Verification(msg.into())
196 }
197
198 pub fn protocol<S: Into<String>>(msg: S) -> Self {
200 Self::Protocol(msg.into())
201 }
202
203 pub fn security<S: Into<String>>(msg: S) -> Self {
205 Self::Security(msg.into())
206 }
207
208 pub fn other<S: Into<String>>(msg: S) -> Self {
210 Self::Other(msg.into())
211 }
212
213 pub fn invalid_service<S: Into<String>>(msg: S) -> Self {
215 Self::InvalidData(msg.into())
216 }
217
218 pub fn is_retryable(&self) -> bool {
220 matches!(
221 self,
222 Self::Network(_) | Self::Timeout(_) | Self::Protocol(_)
223 )
224 }
225
226 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#[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}