1use night_fury_core::NightFuryError;
2use tail_fin_core::SiteError;
3
4#[derive(Debug, thiserror::Error)]
5pub enum TailFinError {
6 #[error("not logged in: auth cookie not found")]
7 AuthRequired,
8
9 #[error("browser error: {0}")]
10 Browser(#[from] NightFuryError),
11
12 #[error("API error: {0}")]
13 Api(String),
14
15 #[error("HTTP {status} {status_text} — body: {body}")]
16 Http {
17 status: u16,
18 status_text: String,
19 body: String,
20 },
21
22 #[error("IO error: {0}")]
23 Io(String),
24
25 #[error("parse error: {0}")]
26 Parse(String),
27
28 #[error("site error: {0}")]
29 Site(#[from] SiteError),
30}
31
32impl From<serde_json::Error> for TailFinError {
33 fn from(e: serde_json::Error) -> Self {
34 TailFinError::Parse(e.to_string())
35 }
36}
37
38#[cfg(test)]
39mod tests {
40 use super::*;
41
42 #[test]
43 fn serde_json_error_converts_to_parse_variant() {
44 let bad_json = "not json at all";
45 let serde_err: serde_json::Error =
46 serde_json::from_str::<serde_json::Value>(bad_json).unwrap_err();
47 let tail_err: TailFinError = serde_err.into();
48 match &tail_err {
49 TailFinError::Parse(msg) => assert!(!msg.is_empty()),
50 other => panic!("expected Parse variant, got {:?}", other),
51 }
52 }
53
54 #[test]
55 fn display_auth_required() {
56 let err = TailFinError::AuthRequired;
57 assert_eq!(err.to_string(), "not logged in: auth cookie not found");
58 }
59
60 #[test]
61 fn display_api_error() {
62 let err = TailFinError::Api("rate limited".into());
63 assert_eq!(err.to_string(), "API error: rate limited");
64 }
65
66 #[test]
67 fn display_http_error_includes_status_and_body() {
68 let err = TailFinError::Http {
69 status: 403,
70 status_text: "Forbidden".into(),
71 body: "invalid token".into(),
72 };
73 let s = err.to_string();
74 assert!(s.contains("403"), "expected status in: {s}");
75 assert!(s.contains("Forbidden"), "expected status text in: {s}");
76 assert!(s.contains("invalid token"), "expected body in: {s}");
77 }
78
79 #[test]
80 fn display_io_error() {
81 let err = TailFinError::Io("file not found".into());
82 assert_eq!(err.to_string(), "IO error: file not found");
83 }
84
85 #[test]
86 fn display_parse_error() {
87 let err = TailFinError::Parse("unexpected token".into());
88 assert_eq!(err.to_string(), "parse error: unexpected token");
89 }
90
91 #[test]
92 fn debug_output_is_not_empty() {
93 let err = TailFinError::AuthRequired;
94 let debug = format!("{:?}", err);
95 assert!(!debug.is_empty());
96 }
97
98 #[test]
99 fn tail_fin_error_from_site_error() {
100 let site_err = SiteError::ManualLoginRequired { site: "shopee" };
103 let tail_err: TailFinError = site_err.into();
104 match tail_err {
105 TailFinError::Site(_) => {}
106 other => panic!("expected Site variant, got {other:?}"),
107 }
108 }
109}