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