Skip to main content

seer_core/
error.rs

1use thiserror::Error;
2
3#[derive(Error, Debug)]
4pub enum SeerError {
5    #[error("WHOIS lookup failed: {0}")]
6    WhoisError(String),
7
8    #[error("WHOIS server not found for TLD: {0}")]
9    WhoisServerNotFound(String),
10
11    #[error("WHOIS connection failed: {0}")]
12    WhoisConnectionFailed(#[from] std::io::Error),
13
14    #[error("RDAP lookup failed: {0}")]
15    RdapError(String),
16
17    #[error("RDAP bootstrap failed: {0}")]
18    RdapBootstrapError(String),
19
20    #[error("DNS resolution failed: {0}")]
21    DnsError(String),
22
23    #[error("DNS resolver error: {0}")]
24    DnsResolverError(#[from] hickory_resolver::error::ResolveError),
25
26    #[error("Invalid domain name: {0}")]
27    InvalidDomain(String),
28
29    #[error("Domain not allowed: TLD '{tld}' is not in the allowlist")]
30    DomainNotAllowed { domain: String, tld: String },
31
32    #[error("Invalid IP address: {0}")]
33    InvalidIpAddress(String),
34
35    #[error("Invalid record type: {0}")]
36    InvalidRecordType(String),
37
38    #[error("HTTP request failed: {0}")]
39    HttpError(String),
40
41    #[error("Reqwest error: {0}")]
42    ReqwestError(#[from] reqwest::Error),
43
44    #[error("JSON parsing failed: {0}")]
45    JsonError(#[from] serde_json::Error),
46
47    #[error("Timeout: {0}")]
48    Timeout(String),
49
50    #[error("Rate limited: {0}")]
51    RateLimited(String),
52
53    #[error("Certificate error: {0}")]
54    CertificateError(String),
55
56    #[error("Bulk operation failed: {context}")]
57    BulkOperationError {
58        context: String,
59        failures: Vec<(String, String)>,
60    },
61
62    #[error("Lookup failed for {domain}: {details}\n\nTip: Try checking the registry directly at: {registry_url}")]
63    LookupFailed {
64        domain: String,
65        details: String,
66        registry_url: String,
67    },
68
69    #[error("{0}")]
70    Other(String),
71
72    #[error("Operation failed after {attempts} attempts: {last_error}")]
73    RetryExhausted { attempts: usize, last_error: String },
74}
75
76impl SeerError {
77    /// Returns a sanitized error message safe for external exposure.
78    /// This hides internal details like server hostnames and raw system errors.
79    pub fn sanitized_message(&self) -> String {
80        match self {
81            SeerError::WhoisError(_) => "WHOIS lookup failed".to_string(),
82            SeerError::WhoisServerNotFound(_) => "WHOIS server not found for this TLD".to_string(),
83            SeerError::WhoisConnectionFailed(_) => "WHOIS connection failed".to_string(),
84            SeerError::RdapError(_) => "RDAP lookup failed".to_string(),
85            SeerError::RdapBootstrapError(_) => {
86                "RDAP service unavailable for this resource".to_string()
87            }
88            SeerError::DnsError(_) => "DNS resolution failed".to_string(),
89            SeerError::DnsResolverError(_) => "DNS resolution failed".to_string(),
90            SeerError::InvalidDomain(domain) => format!("Invalid domain name: {}", domain),
91            SeerError::DomainNotAllowed { tld, .. } => {
92                format!("Domain not allowed: TLD '{}' is not in the allowlist", tld)
93            }
94            SeerError::InvalidIpAddress(ip) => format!("Invalid IP address: {}", ip),
95            SeerError::InvalidRecordType(rt) => format!("Invalid record type: {}", rt),
96            SeerError::HttpError(_) => "HTTP request failed".to_string(),
97            SeerError::ReqwestError(_) => "HTTP request failed".to_string(),
98            SeerError::JsonError(_) => "Response parsing failed".to_string(),
99            SeerError::Timeout(_) => "Operation timed out".to_string(),
100            SeerError::RateLimited(_) => "Rate limited - please try again later".to_string(),
101            SeerError::CertificateError(_) => "Certificate validation failed".to_string(),
102            SeerError::BulkOperationError { .. } => "Bulk operation partially failed".to_string(),
103            SeerError::LookupFailed { domain, .. } => format!("Lookup failed for {}", domain),
104            SeerError::Other(_) => "Operation failed".to_string(),
105            SeerError::RetryExhausted { attempts, .. } => {
106                format!("Operation failed after {} attempts", attempts)
107            }
108        }
109    }
110}
111
112pub type Result<T> = std::result::Result<T, SeerError>;