Skip to main content

libverify_github/
pr_api.rs

1use anyhow::{Context, Result};
2
3use crate::client::GitHubClient;
4use crate::types::{
5    CheckRunsResponse, CombinedStatusResponse, PrCommit, PrFile, PrMetadata, PullRequestListItem,
6    Review, SearchPrItem,
7};
8
9/// Fetch the list of changed files for a PR.
10pub fn get_pr_files(
11    client: &GitHubClient,
12    owner: &str,
13    repo: &str,
14    pr_number: u32,
15) -> Result<Vec<PrFile>> {
16    client.paginate(&format!(
17        "/repos/{owner}/{repo}/pulls/{pr_number}/files?per_page=100"
18    ))
19}
20
21/// Fetch PR metadata.
22pub fn get_pr_metadata(
23    client: &GitHubClient,
24    owner: &str,
25    repo: &str,
26    pr_number: u32,
27) -> Result<PrMetadata> {
28    let path = format!("/repos/{owner}/{repo}/pulls/{pr_number}");
29    let body = client.get(&path)?;
30    serde_json::from_str(&body).context("failed to parse PR metadata")
31}
32
33/// Fetch recent merged PRs for a repository from the closed PR listing.
34pub fn list_recent_merged_prs(
35    client: &GitHubClient,
36    owner: &str,
37    repo: &str,
38    limit: usize,
39) -> Result<Vec<PullRequestListItem>> {
40    let all: Vec<PullRequestListItem> = client.paginate(&format!(
41        "/repos/{owner}/{repo}/pulls?state=closed&sort=updated&direction=desc&per_page=100"
42    ))?;
43    Ok(all
44        .into_iter()
45        .filter(|pr| pr.merged_at.is_some())
46        .take(limit)
47        .collect())
48}
49
50/// Fetch reviews for a PR (paginated).
51pub fn get_pr_reviews(
52    client: &GitHubClient,
53    owner: &str,
54    repo: &str,
55    pr_number: u32,
56) -> Result<Vec<Review>> {
57    client.paginate(&format!(
58        "/repos/{owner}/{repo}/pulls/{pr_number}/reviews?per_page=100"
59    ))
60}
61
62/// Fetch commits for a PR.
63pub fn get_pr_commits(
64    client: &GitHubClient,
65    owner: &str,
66    repo: &str,
67    pr_number: u32,
68) -> Result<Vec<PrCommit>> {
69    client.paginate(&format!(
70        "/repos/{owner}/{repo}/pulls/{pr_number}/commits?per_page=100"
71    ))
72}
73
74/// Fetch check runs for a specific commit ref.
75pub fn get_commit_check_runs(
76    client: &GitHubClient,
77    owner: &str,
78    repo: &str,
79    git_ref: &str,
80) -> Result<CheckRunsResponse> {
81    let path = format!("/repos/{owner}/{repo}/commits/{git_ref}/check-runs?per_page=100");
82    let body = client
83        .get(&path)
84        .context("failed to fetch commit check runs")?;
85    serde_json::from_str(&body).context("failed to parse check runs response")
86}
87
88/// Fetch the combined commit status for a specific commit ref.
89pub fn get_commit_status(
90    client: &GitHubClient,
91    owner: &str,
92    repo: &str,
93    git_ref: &str,
94) -> Result<CombinedStatusResponse> {
95    let path = format!("/repos/{owner}/{repo}/commits/{git_ref}/status");
96    let body = client.get(&path).context("failed to fetch commit status")?;
97    serde_json::from_str(&body).context("failed to parse combined status response")
98}
99
100/// Search for merged PRs within a date range using GitHub Search API.
101pub fn search_merged_prs(
102    client: &GitHubClient,
103    owner: &str,
104    repo: &str,
105    since: &str,
106    until: &str,
107) -> Result<Vec<u32>> {
108    let path = format!(
109        "/search/issues?q=repo:{owner}/{repo}+is:pr+is:merged+merged:{since}..{until}&sort=created&per_page=100"
110    );
111    let items: Vec<SearchPrItem> = client
112        .paginate_search(&path)
113        .context("failed to search merged PRs by date")?;
114    Ok(items.into_iter().map(|item| item.number).collect())
115}
116
117/// Search for merged PRs within a PR number range using GitHub Search API.
118pub fn search_merged_prs_in_range(
119    client: &GitHubClient,
120    owner: &str,
121    repo: &str,
122    start: u32,
123    end: u32,
124) -> Result<Vec<u32>> {
125    let path = format!(
126        "/search/issues?q=repo:{owner}/{repo}+is:pr+is:merged+number:{start}..{end}&sort=created&per_page=100"
127    );
128    let items: Vec<SearchPrItem> = client
129        .paginate_search(&path)
130        .context("failed to search merged PRs by number range")?;
131    Ok(items.into_iter().map(|item| item.number).collect())
132}