rust_assistant/
download.rs

1//! The `download` module.
2//!
3//! Responsible for downloading crates and their contents from sources like crates.io.
4//! This module likely includes structures like `CrateDownloader` which handle the intricacies
5//! of making network requests, handling responses, and processing the downloaded data.
6//!
7use crate::CrateVersion;
8use reqwest::{Client, ClientBuilder};
9use std::io::Read;
10
11/// The `CrateDownloader` struct, responsible for downloading crate files from the internet.
12///
13/// This struct uses the `reqwest` crate's `Client` to make HTTP requests for crate files.
14#[derive(Debug, Default, Clone)]
15pub struct CrateDownloader {
16    client: Client,
17}
18
19impl From<Client> for CrateDownloader {
20    /// Creates a `CrateDownloader` from a `reqwest::Client`.
21    ///
22    /// This allows for custom configuration of the HTTP client used for downloading.
23    ///
24    fn from(client: Client) -> Self {
25        Self { client }
26    }
27}
28
29impl TryFrom<ClientBuilder> for CrateDownloader {
30    type Error = reqwest::Error;
31
32    /// Tries to create a `CrateDownloader` from a `reqwest::ClientBuilder`.
33    ///
34    /// This method attempts to build a `reqwest::Client` and returns a `CrateDownloader` if successful.
35    ///
36    fn try_from(value: ClientBuilder) -> Result<Self, Self::Error> {
37        Ok(Self {
38            client: value.build()?,
39        })
40    }
41}
42
43impl CrateDownloader {
44    /// Asynchronously downloads a crate file from crates.io.
45    ///
46    /// This method constructs the URL for the crate file based on the provided `CrateVersion`
47    /// and uses the internal HTTP client to download it.
48    ///
49    pub async fn download_crate_file(
50        &self,
51        crate_version: &CrateVersion,
52    ) -> anyhow::Result<Vec<u8>> {
53        let url = format!(
54            "https://static.crates.io/crates/{}/{}-{}.crate",
55            crate_version.krate, crate_version.krate, crate_version.version
56        );
57
58        let resp = self.client.get(url).send().await?;
59
60        if !resp.status().is_success() {
61            anyhow::bail!("Http status is not 200: {}", resp.text().await?);
62        }
63
64        let compressed_data = resp.bytes().await?;
65
66        let data = tokio::task::spawn_blocking(move || {
67            let mut dc = flate2::bufread::GzDecoder::new(compressed_data.as_ref());
68            let mut tar_data = Vec::new();
69            dc.read_to_end(&mut tar_data)?;
70
71            Ok::<_, anyhow::Error>(tar_data)
72        })
73        .await??;
74
75        Ok(data)
76    }
77}