use std::ops::ControlFlow;
use crate::retry::{RetryPolicy, is_retriable, retry_sync};
pub enum RepoProbe {
Body(String),
Missing,
AuthDenied,
RateLimited,
Inconclusive(String),
}
pub fn response_is_rate_limited(headers: &reqwest::header::HeaderMap) -> bool {
if headers.contains_key("retry-after") {
return true;
}
headers
.get("x-ratelimit-remaining")
.and_then(|v| v.to_str().ok())
.map(|v| v.trim() == "0")
.unwrap_or(false)
}
pub fn github_repo_probe(
client: &reqwest::blocking::Client,
url: &str,
token: Option<&str>,
policy: &RetryPolicy,
) -> RepoProbe {
let token = token.map(str::to_string);
let outcome = retry_sync(policy, |_attempt| {
let mut b = client
.get(url)
.header("Accept", "application/vnd.github+json")
.header("X-GitHub-Api-Version", "2022-11-28");
if let Some(ref tok) = token
&& !tok.is_empty()
{
b = b.header("Authorization", format!("Bearer {tok}"));
}
match b.send() {
Ok(resp) => {
let code = resp.status().as_u16();
let rate_limited = response_is_rate_limited(resp.headers());
if resp.status().is_success() {
Ok(RepoProbe::Body(resp.text().unwrap_or_default()))
} else if resp.status().is_server_error() {
Err(ControlFlow::Continue(RepoProbe::Inconclusive(format!(
"HTTP {code}"
))))
} else if code == 429 || ((code == 403 || code == 401) && rate_limited) {
Ok(RepoProbe::RateLimited)
} else if code == 404 {
Ok(RepoProbe::Missing)
} else if code == 403 || code == 401 {
Ok(RepoProbe::AuthDenied)
} else {
Ok(RepoProbe::Inconclusive(format!("unexpected HTTP {code}")))
}
}
Err(e) => {
let msg = format!("network failure: {e}");
if is_retriable(&e) {
Err(ControlFlow::Continue(RepoProbe::Inconclusive(msg)))
} else {
Err(ControlFlow::Break(RepoProbe::Inconclusive(msg)))
}
}
}
});
match outcome {
Ok(p) | Err(p) => p,
}
}