1use thiserror::Error;
2
3#[derive(Error, Debug)]
5pub enum GarminError {
6 #[error("Authentication error: {0}")]
7 Authentication(String),
8
9 #[error("Authentication required. Please run 'garmin auth login' first.")]
10 NotAuthenticated,
11
12 #[error("MFA required")]
13 MfaRequired,
14
15 #[error("Rate limited. Please wait before retrying.")]
16 RateLimited,
17
18 #[error("Not found: {0}")]
19 NotFound(String),
20
21 #[error("API error {status}: {message}")]
22 Api { status: u16, message: String },
23
24 #[error("HTTP error: {0}")]
25 Http(#[from] reqwest::Error),
26
27 #[error("Invalid response: {0}")]
28 InvalidResponse(String),
29
30 #[error("JSON error: {0}")]
31 Json(#[from] serde_json::Error),
32
33 #[error("IO error: {0}")]
34 Io(#[from] std::io::Error),
35
36 #[error("Configuration error: {0}")]
37 Config(String),
38
39 #[error("Database error: {0}")]
40 Database(String),
41
42 #[error("Keyring error: {0}")]
43 Keyring(String),
44
45 #[error("Invalid date format: {0}. Expected YYYY-MM-DD")]
46 InvalidDateFormat(String),
47
48 #[error("Invalid parameter: {0}")]
49 InvalidParameter(String),
50
51 #[error("{0}")]
52 Other(String),
53}
54
55pub type Result<T> = std::result::Result<T, GarminError>;
56
57impl GarminError {
58 pub fn auth(msg: impl Into<String>) -> Self {
60 Self::Authentication(msg.into())
61 }
62
63 pub fn config(msg: impl Into<String>) -> Self {
65 Self::Config(msg.into())
66 }
67
68 pub fn invalid_response(msg: impl Into<String>) -> Self {
70 Self::InvalidResponse(msg.into())
71 }
72
73 pub fn invalid_param(msg: impl Into<String>) -> Self {
75 Self::InvalidParameter(msg.into())
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn test_error_display() {
85 let err = GarminError::Authentication("Invalid credentials".to_string());
86 assert_eq!(err.to_string(), "Authentication error: Invalid credentials");
87 }
88
89 #[test]
90 fn test_not_authenticated_error() {
91 let err = GarminError::NotAuthenticated;
92 assert!(err.to_string().contains("garmin auth login"));
93 }
94
95 #[test]
96 fn test_rate_limited_error() {
97 let err = GarminError::RateLimited;
98 assert!(err.to_string().contains("Rate limited"));
99 }
100
101 #[test]
102 fn test_invalid_date_format_error() {
103 let err = GarminError::InvalidDateFormat("not-a-date".to_string());
104 assert!(err.to_string().contains("not-a-date"));
105 assert!(err.to_string().contains("YYYY-MM-DD"));
106 }
107
108 #[test]
109 fn test_error_constructors() {
110 let auth_err = GarminError::auth("test auth");
111 assert!(matches!(auth_err, GarminError::Authentication(_)));
112
113 let config_err = GarminError::config("test config");
114 assert!(matches!(config_err, GarminError::Config(_)));
115
116 let response_err = GarminError::invalid_response("bad response");
117 assert!(matches!(response_err, GarminError::InvalidResponse(_)));
118
119 let param_err = GarminError::invalid_param("bad param");
120 assert!(matches!(param_err, GarminError::InvalidParameter(_)));
121 }
122}