libverify_github/
ossinsight.rs1use 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}