use smol for client

This commit is contained in:
Pascal Engélibert 2026-03-08 10:56:34 +01:00
commit 9c70eea4f1
5 changed files with 608 additions and 468 deletions

View file

@ -3,9 +3,9 @@ use std::{collections::HashSet, io::Write, path::PathBuf};
use crate::config::Config;
use rand::Rng;
use reqwest::Client;
use serde::Deserialize;
use sha2::Digest;
use trillium_client::Client;
pub const USER_AGENT: &str = "Blindforge";
pub const MAX_PAGE: u32 = 256;
@ -19,8 +19,36 @@ struct RepoGetTagResponse {
tarball_url: String,
}
pub fn make_client() -> Result<Client, reqwest::Error> {
reqwest::ClientBuilder::new().user_agent(USER_AGENT).build()
#[derive(Debug)]
pub enum ClientError {
Client(trillium_client::Error),
Http(trillium_client::UnexpectedStatusError),
Parse(trillium_client::ClientSerdeError),
}
impl From<trillium_client::Error> for ClientError {
fn from(value: trillium_client::Error) -> Self {
Self::Client(value)
}
}
impl From<trillium_client::UnexpectedStatusError> for ClientError {
fn from(value: trillium_client::UnexpectedStatusError) -> Self {
Self::Http(value)
}
}
impl From<trillium_client::ClientSerdeError> for ClientError {
fn from(value: trillium_client::ClientSerdeError) -> Self {
Self::Parse(value)
}
}
pub fn make_client() -> Client {
Client::new(trillium_native_tls::NativeTlsConfig::<
trillium_smol::ClientConfig,
>::default())
.with_default_header("User-Agent", USER_AGENT)
}
pub async fn fetch_repo_archive_url(
@ -30,12 +58,12 @@ pub async fn fetch_repo_archive_url(
repo: &str,
tag: &str,
token: Option<&str>,
) -> Result<String, reqwest::Error> {
let mut req = client.get(&format!("{url}/api/v1/repos/{owner}/{repo}/tags/{tag}"));
) -> Result<String, ClientError> {
let mut req = client.get(format!("{url}/api/v1/repos/{owner}/{repo}/tags/{tag}"));
if let Some(token) = token {
req = req.header("Authorization", format!("token {token}"));
req = req.with_request_header("Authorization", format!("token {token}"));
}
let res: RepoGetTagResponse = req.send().await?.error_for_status()?.json().await?;
let res: RepoGetTagResponse = req.await?.success()?.response_json().await?;
Ok(res.tarball_url)
}
@ -63,27 +91,45 @@ async fn fetch_repo_tree_at_commit_page(
commit_hash: &str,
token: Option<&str>,
page: u32,
) -> Result<GitTreeResponse, reqwest::Error> {
let mut req = client.get(&format!(
) -> Result<GitTreeResponse, ClientError> {
let mut req = client.get(format!(
"{url}/api/v1/repos/{owner}/{repo}/git/trees/{commit_hash}?recursive=true&page={page}"
));
if let Some(token) = token {
req = req.header("Authorization", format!("token {token}"));
req = req.with_request_header("Authorization", format!("token {token}"));
}
// TODO ensure we don't use too much memory
req.send().await?.error_for_status()?.json().await
Ok(req.await?.success()?.response_json().await?)
}
#[derive(Debug)]
pub enum FetchRepoError {
CannotCreateDir(std::io::Error),
TooManyEntries,
Reqwest(reqwest::Error),
Client(ClientError),
}
impl From<reqwest::Error> for FetchRepoError {
fn from(value: reqwest::Error) -> Self {
Self::Reqwest(value)
impl From<ClientError> for FetchRepoError {
fn from(value: ClientError) -> Self {
Self::Client(value)
}
}
impl From<trillium_client::Error> for FetchRepoError {
fn from(value: trillium_client::Error) -> Self {
Self::Client(value.into())
}
}
impl From<trillium_client::UnexpectedStatusError> for FetchRepoError {
fn from(value: trillium_client::UnexpectedStatusError) -> Self {
Self::Client(value.into())
}
}
impl From<trillium_client::ClientSerdeError> for FetchRepoError {
fn from(value: trillium_client::ClientSerdeError) -> Self {
Self::Client(value.into())
}
}
@ -152,7 +198,7 @@ pub async fn fetch_repo_files(
let mut hasher = sha2::Sha256::default();
let repo_key: [u8; 32] = rand::rng().random();
hasher.update(&repo_key);
hasher.update(repo_key);
let repo_id: [u8; 32] = hasher.finalize_reset().into();
let mut repo_id_str = [0; 24];
base64_turbo::URL_SAFE
@ -168,12 +214,12 @@ pub async fn fetch_repo_files(
let mut file_index = HashSet::new();
for file in repo_index.files.iter() {
let mut req = client.get(&file.url);
let mut req = client.get(file.url.clone());
if let Some(token) = token {
req = req.header("Authorization", format!("token {token}"));
req = req.with_request_header("Authorization", format!("token {token}"));
}
// TODO ensure we don't use too much memory
let blob: GitBlob = req.send().await?.error_for_status()?.json().await?;
let blob: GitBlob = req.await?.success()?.response_json().await?;
if base64_turbo::STANDARD.estimate_decoded_len(blob.content.len()) as u64 > MAX_FILE_SIZE {
continue;
}
@ -183,7 +229,7 @@ pub async fn fetch_repo_files(
if file_index.insert(file_name) {
let mut file_name_str = [0; 44];
base64_turbo::URL_SAFE
.encode_into(&file_name, &mut file_name_str)
.encode_into(file_name, &mut file_name_str)
.expect("unreachable");
let file_name_str = str::from_utf8(&file_name_str).expect("unreachable");
if let Ok(mut file) = std::fs::OpenOptions::new()