Skip to main content

cc_audit/remote/
error.rs

1use thiserror::Error;
2
3/// Errors related to remote repository scanning
4#[derive(Debug, Error)]
5pub enum RemoteError {
6    /// Git clone operation failed
7    #[error("Git clone failed for {url}: {message}")]
8    CloneFailed { url: String, message: String },
9
10    /// Invalid repository URL format
11    #[error("Invalid repository URL: {0}")]
12    InvalidUrl(String),
13
14    /// Repository not found (404)
15    #[error("Repository not found: {0}")]
16    NotFound(String),
17
18    /// Authentication required for private repository
19    #[error("Authentication required for private repository: {0}")]
20    AuthRequired(String),
21
22    /// GitHub API rate limit exceeded
23    #[error("GitHub rate limit exceeded, retry after {reset_at}")]
24    RateLimitExceeded { reset_at: String },
25
26    /// Network/IO error
27    #[error("Network error: {0}")]
28    Network(#[from] std::io::Error),
29
30    /// HTTP request error
31    #[error("HTTP error {status}: {message}")]
32    Http { status: u16, message: String },
33
34    /// Failed to parse awesome-claude-code README
35    #[error("Failed to parse awesome-claude-code: {0}")]
36    ParseError(String),
37
38    /// Temporary directory creation failed
39    #[error("Temporary directory error: {0}")]
40    TempDir(String),
41
42    /// Git command not found
43    #[error("Git command not found. Please install git.")]
44    GitNotFound,
45
46    /// Clone timeout exceeded
47    #[error("Clone timeout exceeded for {url} (timeout: {timeout_secs}s)")]
48    CloneTimeout { url: String, timeout_secs: u64 },
49
50    /// Repository too large
51    #[error("Repository too large: {url} (size: {size_mb}MB, limit: {limit_mb}MB)")]
52    RepositoryTooLarge {
53        url: String,
54        size_mb: u64,
55        limit_mb: u64,
56    },
57}
58
59impl RemoteError {
60    /// Check if error is retryable
61    pub fn is_retryable(&self) -> bool {
62        matches!(
63            self,
64            RemoteError::RateLimitExceeded { .. }
65                | RemoteError::Network(_)
66                | RemoteError::CloneTimeout { .. }
67        )
68    }
69
70    /// Check if error is due to authentication issues
71    pub fn is_auth_error(&self) -> bool {
72        matches!(self, RemoteError::AuthRequired(_))
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn test_clone_failed_error() {
82        let err = RemoteError::CloneFailed {
83            url: "https://github.com/user/repo".to_string(),
84            message: "Connection refused".to_string(),
85        };
86        assert!(err.to_string().contains("github.com/user/repo"));
87        assert!(err.to_string().contains("Connection refused"));
88    }
89
90    #[test]
91    fn test_is_retryable() {
92        assert!(
93            RemoteError::RateLimitExceeded {
94                reset_at: "2026-01-25T12:00:00Z".to_string()
95            }
96            .is_retryable()
97        );
98
99        assert!(
100            RemoteError::CloneTimeout {
101                url: "https://github.com/user/repo".to_string(),
102                timeout_secs: 60
103            }
104            .is_retryable()
105        );
106
107        assert!(!RemoteError::InvalidUrl("bad".to_string()).is_retryable());
108        assert!(!RemoteError::NotFound("repo".to_string()).is_retryable());
109    }
110
111    #[test]
112    fn test_is_auth_error() {
113        assert!(
114            RemoteError::AuthRequired("https://github.com/private/repo".to_string())
115                .is_auth_error()
116        );
117        assert!(!RemoteError::NotFound("repo".to_string()).is_auth_error());
118    }
119}