securegit 0.8.5

Zero-trust git replacement with 12 built-in security scanners, LLM redteam bridge, universal undo, durable backups, and a 50-tool MCP server
Documentation
use anyhow::{Context, Result};
use reqwest::Client;
use std::path::Path;
use tokio::io::AsyncWriteExt;

pub struct Downloader {
    client: Client,
}

impl Downloader {
    pub fn new() -> Self {
        Self {
            client: Client::builder()
                .timeout(std::time::Duration::from_secs(300))
                .build()
                .expect("failed to build HTTP client"),
        }
    }

    pub async fn download(&self, url: &str, dest: &Path) -> Result<()> {
        self.download_with_headers(url, dest, &reqwest::header::HeaderMap::new())
            .await
    }

    pub async fn download_with_headers(
        &self,
        url: &str,
        dest: &Path,
        headers: &reqwest::header::HeaderMap,
    ) -> Result<()> {
        let mut request = self.client.get(url);
        if !headers.is_empty() {
            request = request.headers(headers.clone());
        }

        let response = request
            .send()
            .await
            .context("Failed to send HTTP request")?;

        if !response.status().is_success() {
            anyhow::bail!(
                "Download failed: HTTP {} for URL: {}",
                response.status(),
                url
            );
        }

        let bytes = response
            .bytes()
            .await
            .context("Failed to read response bytes")?;
        let mut file = tokio::fs::File::create(dest)
            .await
            .context(format!("Failed to create file: {}", dest.display()))?;
        file.write_all(&bytes)
            .await
            .context("Failed to write to file")?;
        file.sync_all().await.context("Failed to sync file")?;

        Ok(())
    }
}

impl Default for Downloader {
    fn default() -> Self {
        Self::new()
    }
}