1use thiserror::Error;
4
5pub type Result<T> = std::result::Result<T, CompatError>;
7
8#[derive(Debug, Error)]
10pub enum CompatError {
11 #[error("user not found: {0}")]
13 UserNotFound(String),
14
15 #[error("username already exists: {0}")]
17 UsernameExists(String),
18
19 #[error("invalid username: {0}")]
21 InvalidUsername(String),
22
23 #[error("token not found")]
25 TokenNotFound,
26
27 #[error("invalid token format")]
29 InvalidTokenFormat,
30
31 #[error("token expired")]
33 TokenExpired,
34
35 #[error("invalid token")]
37 InvalidToken,
38
39 #[error("insufficient scope: requires {0:?}")]
41 InsufficientScope(crate::token::TokenScope),
42
43 #[error("SSH key not found")]
45 SshKeyNotFound,
46
47 #[error("invalid SSH key format: {0}")]
49 InvalidSshKey(String),
50
51 #[error("SSH key already exists with fingerprint: {0}")]
53 SshKeyExists(String),
54
55 #[error("release not found: {0}")]
57 ReleaseNotFound(String),
58
59 #[error("release already exists for tag: {0}")]
61 ReleaseExists(String),
62
63 #[error("asset not found: {0}")]
65 AssetNotFound(String),
66
67 #[error("asset already exists: {0}")]
69 AssetExists(String),
70
71 #[error("path not found: {0}")]
73 PathNotFound(String),
74
75 #[error("invalid ref: {0}")]
77 InvalidRef(String),
78
79 #[error("archive generation failed: {0}")]
81 ArchiveFailed(String),
82
83 #[error("rate limit exceeded, resets at {0}")]
85 RateLimitExceeded(u64),
86
87 #[error("storage error: {0}")]
89 Storage(String),
90
91 #[error("crypto error: {0}")]
93 Crypto(String),
94}
95
96impl CompatError {
97 pub fn status_code(&self) -> u16 {
99 match self {
100 Self::UserNotFound(_) => 404,
101 Self::UsernameExists(_) => 409,
102 Self::InvalidUsername(_) => 422,
103 Self::TokenNotFound => 401,
104 Self::InvalidTokenFormat => 401,
105 Self::TokenExpired => 401,
106 Self::InvalidToken => 401,
107 Self::InsufficientScope(_) => 403,
108 Self::SshKeyNotFound => 404,
109 Self::InvalidSshKey(_) => 422,
110 Self::SshKeyExists(_) => 409,
111 Self::ReleaseNotFound(_) => 404,
112 Self::ReleaseExists(_) => 409,
113 Self::AssetNotFound(_) => 404,
114 Self::AssetExists(_) => 409,
115 Self::PathNotFound(_) => 404,
116 Self::InvalidRef(_) => 422,
117 Self::ArchiveFailed(_) => 500,
118 Self::RateLimitExceeded(_) => 429,
119 Self::Storage(_) => 500,
120 Self::Crypto(_) => 500,
121 }
122 }
123
124 pub fn github_message(&self) -> &str {
126 match self {
127 Self::UserNotFound(_) => "Not Found",
128 Self::UsernameExists(_) => "Validation Failed",
129 Self::InvalidUsername(_) => "Validation Failed",
130 Self::TokenNotFound => "Bad credentials",
131 Self::InvalidTokenFormat => "Bad credentials",
132 Self::TokenExpired => "Bad credentials",
133 Self::InvalidToken => "Bad credentials",
134 Self::InsufficientScope(_) => "Forbidden",
135 Self::SshKeyNotFound => "Not Found",
136 Self::InvalidSshKey(_) => "Validation Failed",
137 Self::SshKeyExists(_) => "Validation Failed",
138 Self::ReleaseNotFound(_) => "Not Found",
139 Self::ReleaseExists(_) => "Validation Failed",
140 Self::AssetNotFound(_) => "Not Found",
141 Self::AssetExists(_) => "Validation Failed",
142 Self::PathNotFound(_) => "Not Found",
143 Self::InvalidRef(_) => "Validation Failed",
144 Self::ArchiveFailed(_) => "Server Error",
145 Self::RateLimitExceeded(_) => "API rate limit exceeded",
146 Self::Storage(_) => "Server Error",
147 Self::Crypto(_) => "Server Error",
148 }
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155
156 #[test]
157 fn test_error_status_codes() {
158 assert_eq!(CompatError::UserNotFound("test".into()).status_code(), 404);
159 assert_eq!(CompatError::TokenNotFound.status_code(), 401);
160 assert_eq!(CompatError::RateLimitExceeded(0).status_code(), 429);
161 }
162}