Skip to content

Commit 4d0ad80

Browse files
committed
fix(swift): handle GitHub API rate limit errors gracefully
Parse error objects from GitHub API instead of panicking on unexpected JSON structure. Rate-limited responses now produce descriptive error messages instead of cryptic serde deserialization failures.
1 parent 4cbd309 commit 4d0ad80

File tree

2 files changed

+35
-3
lines changed

2 files changed

+35
-3
lines changed

crates/deps-swift/src/error.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ impl SwiftError {
4949
message: message.into(),
5050
}
5151
}
52+
53+
pub fn github_api_error(message: impl Into<String>) -> Self {
54+
Self::GitHubApiError {
55+
status: 0,
56+
message: message.into(),
57+
}
58+
}
5259
}
5360

5461
impl From<deps_core::DepsError> for SwiftError {

crates/deps-swift/src/registry.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,26 @@ struct GithubTag {
101101
name: String,
102102
}
103103

104+
/// GitHub API error response (rate limit, not found, etc.).
105+
#[derive(Deserialize)]
106+
struct GithubErrorResponse {
107+
message: String,
108+
}
109+
104110
/// Parses GitHub tags API response into SwiftVersion list.
111+
///
112+
/// GitHub returns an error object instead of array when rate-limited or on
113+
/// other errors. Detect this and return a descriptive error.
105114
fn parse_tags_response(data: &[u8]) -> Result<Vec<SwiftVersion>> {
106-
let tags: Vec<GithubTag> = serde_json::from_slice(data)?;
115+
let tags: Vec<GithubTag> = match serde_json::from_slice(data) {
116+
Ok(t) => t,
117+
Err(_) => {
118+
if let Ok(err) = serde_json::from_slice::<GithubErrorResponse>(data) {
119+
return Err(SwiftError::github_api_error(&err.message).into());
120+
}
121+
return Ok(vec![]);
122+
}
123+
};
107124

108125
let mut versions_with_parsed: Vec<(SwiftVersion, semver::Version)> = tags
109126
.into_iter()
@@ -284,9 +301,17 @@ mod tests {
284301
}
285302

286303
#[test]
287-
fn test_parse_tags_invalid_json_returns_error() {
288-
let result = parse_tags_response(b"not json");
304+
fn test_parse_tags_invalid_json_returns_empty() {
305+
let result = parse_tags_response(b"not json").unwrap();
306+
assert!(result.is_empty());
307+
}
308+
309+
#[test]
310+
fn test_parse_tags_github_rate_limit_returns_error() {
311+
let json = r#"{"message":"API rate limit exceeded for 1.2.3.4."}"#;
312+
let result = parse_tags_response(json.as_bytes());
289313
assert!(result.is_err());
314+
assert!(result.unwrap_err().to_string().contains("rate limit"));
290315
}
291316

292317
#[test]

0 commit comments

Comments
 (0)