codeberg.rs

 1use crate::{git_author::GitAuthor, http::HttpClient};
 2use anyhow::{bail, Context, Result};
 3use futures::AsyncReadExt;
 4use isahc::{config::Configurable, AsyncBody, Request};
 5use serde::Deserialize;
 6use std::sync::Arc;
 7
 8#[derive(Debug, Deserialize)]
 9struct CommitDetails {
10    commit: Commit,
11    author: Option<User>,
12}
13
14#[derive(Debug, Deserialize)]
15struct Commit {
16    author: Author,
17}
18
19#[derive(Debug, Deserialize)]
20struct Author {
21    name: String,
22    email: String,
23    date: String,
24}
25
26#[derive(Debug, Deserialize)]
27struct User {
28    pub login: String,
29    pub id: u64,
30    pub avatar_url: String,
31}
32
33pub async fn fetch_codeberg_commit_author(
34    repo_owner: &str,
35    repo: &str,
36    commit: &str,
37    client: &Arc<dyn HttpClient>,
38) -> Result<Option<GitAuthor>> {
39    let url = format!("https://codeberg.org/api/v1/repos/{repo_owner}/{repo}/git/commits/{commit}");
40
41    let mut request = Request::get(&url)
42        .redirect_policy(isahc::config::RedirectPolicy::Follow)
43        .header("Content-Type", "application/json");
44
45    if let Ok(codeberg_token) = std::env::var("CODEBERG_TOKEN") {
46        request = request.header("Authorization", format!("Bearer {}", codeberg_token));
47    }
48
49    let mut response = client
50        .send(request.body(AsyncBody::default())?)
51        .await
52        .with_context(|| format!("error fetching Codeberg commit details at {:?}", url))?;
53
54    let mut body = Vec::new();
55    response.body_mut().read_to_end(&mut body).await?;
56
57    if response.status().is_client_error() {
58        let text = String::from_utf8_lossy(body.as_slice());
59        bail!(
60            "status error {}, response: {text:?}",
61            response.status().as_u16()
62        );
63    }
64
65    let body_str = std::str::from_utf8(&body)?;
66
67    serde_json::from_str::<CommitDetails>(body_str)
68        .map(|codeberg_commit| {
69            if let Some(author) = codeberg_commit.author {
70                Some(GitAuthor {
71                    avatar_url: author.avatar_url,
72                })
73            } else {
74                None
75            }
76        })
77        .context("deserializing Codeberg commit details failed")
78}