Skip to main content

libverify_github/
ossinsight.rs

1use anyhow::{Context, Result, bail};
2use reqwest::blocking::Client;
3use reqwest::header::{ACCEPT, HeaderMap, HeaderValue, USER_AGENT};
4use serde::{Deserialize, Serialize};
5
6const BASE_URL: &str = "https://api.ossinsight.io/v1";
7
8pub struct OssInsightClient {
9    client: Client,
10}
11
12impl OssInsightClient {
13    pub fn new() -> Result<Self> {
14        Self::with_user_agent("libverify-github/0.1.0")
15    }
16
17    pub fn with_user_agent(user_agent: &str) -> Result<Self> {
18        let mut headers = HeaderMap::new();
19        headers.insert(ACCEPT, HeaderValue::from_static("application/json"));
20        headers.insert(
21            USER_AGENT,
22            HeaderValue::from_str(user_agent).context("invalid User-Agent")?,
23        );
24
25        let client = Client::builder()
26            .default_headers(headers)
27            .build()
28            .context("failed to create OSS Insight HTTP client")?;
29        Ok(Self { client })
30    }
31
32    pub fn ranking_by_prs(
33        &self,
34        collection_id: u64,
35        period: &str,
36    ) -> Result<Vec<CollectionRepoRank>> {
37        self.get_rows(&format!(
38            "{BASE_URL}/collections/{collection_id}/ranking_by_prs/?period={period}"
39        ))
40    }
41
42    pub fn pull_request_creators(
43        &self,
44        owner: &str,
45        repo: &str,
46        page_size: u32,
47    ) -> Result<Vec<PullRequestCreator>> {
48        self.get_rows(&format!(
49            "{BASE_URL}/repos/{owner}/{repo}/pull_request_creators/?sort=prs-desc&exclude_bots=true&page=1&page_size={page_size}"
50        ))
51    }
52
53    fn get_rows<T: for<'de> Deserialize<'de>>(&self, url: &str) -> Result<Vec<T>> {
54        let response = self
55            .client
56            .get(url)
57            .send()
58            .context("OSS Insight request failed")?;
59        let status = response.status();
60        if !status.is_success() {
61            bail!(
62                "OSS Insight API error: {} {}",
63                status.as_u16(),
64                status.canonical_reason().unwrap_or("Unknown")
65            );
66        }
67
68        let payload: SqlRowsResponse<T> = response
69            .json()
70            .context("failed to parse OSS Insight response")?;
71        Ok(payload.data.rows)
72    }
73}
74
75#[derive(Debug, Clone, Deserialize)]
76struct SqlRowsResponse<T> {
77    data: SqlRows<T>,
78}
79
80#[derive(Debug, Clone, Deserialize)]
81struct SqlRows<T> {
82    rows: Vec<T>,
83}
84
85#[derive(Debug, Clone, Deserialize, Serialize)]
86pub struct CollectionRepoRank {
87    pub repo_id: String,
88    pub repo_name: String,
89    pub current_period_growth: String,
90    pub past_period_growth: String,
91    pub growth_pop: String,
92    pub rank_pop: String,
93    pub total: String,
94    pub current_period_rank: String,
95    pub past_period_rank: String,
96}
97
98#[derive(Debug, Clone, Deserialize, Serialize)]
99pub struct PullRequestCreator {
100    pub id: String,
101    pub login: String,
102    pub name: String,
103    pub prs: String,
104    pub first_pr_opened_at: String,
105    pub first_pr_merged_at: String,
106}