garmin_cli/
error.rs

1use thiserror::Error;
2
3/// Main error type for garmin-cli
4#[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    /// Create an authentication error from a message
59    pub fn auth(msg: impl Into<String>) -> Self {
60        Self::Authentication(msg.into())
61    }
62
63    /// Create a configuration error from a message
64    pub fn config(msg: impl Into<String>) -> Self {
65        Self::Config(msg.into())
66    }
67
68    /// Create an invalid response error from a message
69    pub fn invalid_response(msg: impl Into<String>) -> Self {
70        Self::InvalidResponse(msg.into())
71    }
72
73    /// Create an invalid parameter error from a message
74    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}