use super::{API, GitHubClient, urlencoding};
use rootle_provider::ProviderResult;
impl GitHubClient {
pub fn log(
&self,
owner: &str,
repo: &str,
path: Option<&str>,
ref_: Option<&str>,
limit: Option<usize>,
) -> ProviderResult<(Vec<rootle_provider::LogEntry>, bool)> {
#[derive(serde::Deserialize)]
struct CommitItem {
sha: String,
commit: CommitDetail,
}
#[derive(serde::Deserialize)]
struct CommitDetail {
message: String,
author: CommitAuthor,
}
#[derive(serde::Deserialize)]
struct CommitAuthor {
name: String,
date: String,
}
let want = limit.unwrap_or(50).min(99);
let mut url = format!("{API}/repos/{owner}/{repo}/commits?per_page={}", want + 1);
if let Some(p) = path {
url.push_str(&format!("&path={}", urlencoding(p)));
}
if let Some(r) = ref_ {
url.push_str(&format!("&sha={}", urlencoding(r)));
}
let mut items: Vec<CommitItem> = self.get(&url)?;
let truncated = items.len() > want;
items.truncate(want);
Ok((
items
.into_iter()
.map(|c| rootle_provider::LogEntry {
sha: c.sha,
subject: c.commit.message.lines().next().unwrap_or("").to_string(),
author: c.commit.author.name,
date: c.commit.author.date,
})
.collect(),
truncated,
))
}
pub fn commit(
&self,
owner: &str,
repo: &str,
sha: &str,
) -> ProviderResult<crate::types::CommitResponse> {
const FILES_PER_PAGE: usize = 100;
const FILE_BUDGET: usize = rootle_provider::RENDER_BUDGET;
let mut url = reqwest::Url::parse(API).expect("GitHub API URL");
url.path_segments_mut()
.expect("HTTP URL supports path segments")
.extend(["repos", owner, repo, "commits", sha]);
url.query_pairs_mut()
.append_pair("per_page", &FILES_PER_PAGE.to_string());
let first = self.get_page::<crate::types::CommitResponse>(url.as_str())?;
let mut response = first.body;
let mut has_next = first.has_next;
let mut page = 1u32;
url.path_segments_mut()
.expect("HTTP URL supports path segments")
.pop()
.push(&response.sha);
while has_next && response.files.len() < FILE_BUDGET {
page += 1;
url.query_pairs_mut()
.clear()
.append_pair("per_page", &FILES_PER_PAGE.to_string())
.append_pair("page", &page.to_string());
let next = self.get_page::<crate::types::CommitResponse>(url.as_str())?;
if next.body.sha != response.sha {
return Err(rootle_provider::ProviderError::other(
"commit changed across file pages",
));
}
response.files.extend(next.body.files);
has_next = next.has_next;
}
response.truncated = has_next || response.files.len() > FILE_BUDGET;
response.files.truncate(FILE_BUDGET);
Ok(response)
}
}