1#[cfg(feature = "browser")]
4use night_fury_core::NightFuryError;
5
6#[derive(Debug, thiserror::Error)]
8pub enum SiteError {
9 #[error("refresh failed on site '{site}': {reason}")]
10 RefreshFailed { site: &'static str, reason: String },
11
12 #[error("validation failed on site '{site}': {reason}")]
13 ValidationFailed { site: &'static str, reason: String },
14
15 #[error("manual login required for site '{site}' — automated login not supported")]
16 ManualLoginRequired { site: &'static str },
17
18 #[error("login failed: {0:?}")]
19 LoginFailed(AuthFailureKind),
20
21 #[cfg(feature = "browser")]
22 #[error("browser error during site operation: {0}")]
23 Browser(#[from] NightFuryError),
24}
25
26#[derive(Debug, Clone)]
28pub enum AuthFailureKind {
29 CookieExpired,
31 CredentialsRejected,
33 RateLimited { retry_after: std::time::Duration },
35 AntibotBlock { challenge_type: String },
37 AccountSuspended,
39 Other(String),
41}
42
43#[cfg(test)]
44mod tests {
45 use super::*;
46
47 #[test]
48 fn site_error_displays_with_site_id() {
49 let err = SiteError::RefreshFailed {
50 site: "twitter",
51 reason: "auth_token missing".into(),
52 };
53 let msg = err.to_string();
54 assert!(msg.contains("twitter"));
55 assert!(msg.contains("auth_token missing"));
56 }
57
58 #[test]
59 fn auth_failure_kind_debug() {
60 let kind = AuthFailureKind::RateLimited {
61 retry_after: std::time::Duration::from_secs(60),
62 };
63 let debug = format!("{kind:?}");
64 assert!(debug.contains("RateLimited"));
65 }
66
67 #[test]
68 fn auth_failure_kind_variants_exist() {
69 let _ = AuthFailureKind::CookieExpired;
70 let _ = AuthFailureKind::CredentialsRejected;
71 let _ = AuthFailureKind::AccountSuspended;
72 let _ = AuthFailureKind::AntibotBlock {
73 challenge_type: "captcha".into(),
74 };
75 let _ = AuthFailureKind::Other("custom".into());
76 }
77}