tcfetch/
gh.rs

1use crate::utils::{get_json, url};
2use crate::Result;
3use reqwest;
4use serde_derive::Deserialize;
5use std::collections::BTreeMap;
6
7#[derive(Debug, Deserialize)]
8pub struct ChecksResponse {
9    pub total_count: u64,
10    pub check_runs: Vec<CheckRun>,
11}
12
13#[derive(Debug, Deserialize)]
14pub struct CheckRun {
15    pub id: u64,
16    pub head_sha: String,
17    pub node_id: String,
18    pub external_id: Option<String>,
19    pub url: String,
20    pub html_url: Option<String>,
21    pub details_url: Option<String>,
22    pub status: RunStatus,
23    pub conclusion: Option<RunConclusion>, //TODO: enum,
24    pub started_at: Option<String>,
25    pub completed_at: Option<String>,
26    pub output: CheckOutput,
27    pub name: String,
28    pub check_suite: Option<CheckSuite>,
29    pub app: Option<GithubApp>,
30    pub pull_requests: Vec<PullRequestMinimal>,
31}
32
33#[derive(Debug, Deserialize)]
34#[serde(rename_all = "snake_case")]
35pub enum RunStatus {
36    Queued,
37    InProgress,
38    Completed,
39}
40
41#[derive(Debug, Deserialize)]
42#[serde(rename_all = "snake_case")]
43pub enum RunConclusion {
44    Success,
45    Failure,
46    Neutral,
47    Cancelled,
48    Skipped,
49    TimedOut,
50    ActionRequired,
51}
52
53#[derive(Debug, Deserialize)]
54pub struct CheckOutput {
55    pub title: Option<String>,
56    pub summary: Option<String>,
57    pub text: Option<String>,
58    pub annotations_count: u64,
59    pub annotations_url: String,
60}
61
62#[derive(Debug, Deserialize)]
63pub struct CheckSuite {
64    pub id: u64,
65}
66
67#[derive(Debug, Deserialize)]
68pub struct GithubApp {
69    pub id: u64,
70    pub slug: String,
71    pub node_id: String,
72    pub owner: Option<SimpleUser>,
73    pub name: String,
74    pub description: Option<String>,
75    pub external_url: String,
76    pub html_url: String,
77    pub created_at: String,
78    pub updated_at: String,
79    pub permissions: BTreeMap<String, String>,
80    pub events: Vec<String>,
81}
82
83#[derive(Debug, Deserialize)]
84pub struct SimpleUser {
85    pub login: String,
86    pub id: u64,
87    pub node_id: String,
88    pub url: String,
89    pub repos_url: String,
90    pub events_url: String,
91    pub avatar_url: String,
92    pub gravatar_id: Option<String>,
93    pub html_url: String,
94    pub followers_url: String,
95    pub following_url: String,
96    pub gists_url: String,
97    pub starred_url: String,
98    pub subscriptions_url: String,
99    pub organizations_url: String,
100    pub received_events_url: String,
101    #[serde(rename = "type")]
102    pub user_type: String,
103    pub site_admin: bool,
104    pub starred_at: Option<String>,
105}
106
107#[derive(Debug, Deserialize)]
108pub struct Permissions {
109    pub metadata: String,    // TODO: enum
110    pub contents: String,    // TODO: enum
111    pub issues: String,      // TODO: enum
112    pub single_file: String, // TODO: enum
113}
114
115#[derive(Debug, Deserialize)]
116pub struct PullRequestMinimal {
117    pub url: String,
118    pub id: u64,
119    pub number: u64,
120    pub head: Ref,
121}
122
123#[derive(Debug, Deserialize)]
124pub struct Ref {
125    #[serde(rename = "ref")]
126    pub ref_name: String,
127    pub sha: String,
128    pub repo: Repo,
129}
130
131#[derive(Debug, Deserialize)]
132pub struct Repo {
133    pub id: u64,
134    pub url: String,
135    pub name: String,
136}
137
138pub fn get_checks(
139    client: &reqwest::blocking::Client,
140    owner: &str,
141    repo: &str,
142    sha1: &str,
143) -> Result<Vec<CheckRun>> {
144    let url_suffix = format!("repos/{}/{}/commits/{}/check-runs", owner, repo, sha1);
145    let mut page = 0;
146    let mut checks = Vec::new();
147    let mut checks_total: Option<u64> = None;
148    while checks_total.is_none() || checks_total != Some(checks.len() as u64) {
149        page += 1;
150        let base_url = &url("https://api.github.com/", &url_suffix);
151        let mut query = vec![
152            ("per_page".into(), "100".into()),
153            ("filter".into(), "all".into()),
154        ];
155        if page > 1 {
156            query.push(("page".into(), page.to_string()));
157        }
158        let checks_resp: ChecksResponse = get_json(
159            client,
160            base_url,
161            Some(query),
162            Some(vec![
163                ("user-agent".to_string(), "tcfetch/0.4".to_string()),
164                (
165                    "Accept".to_string(),
166                    "application/vnd.github+json".to_string(),
167                ),
168                ("X-GitHub-Api-Version".to_string(), "2022-11-28".to_string()),
169            ]),
170        )?;
171        checks_total = Some(checks_resp.total_count);
172        checks.extend(checks_resp.check_runs.into_iter())
173    }
174    Ok(checks)
175}