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}