Skip to main content

teeny_http/
fetch.rs

1/*
2 * Copyright (c) 2026 Teenygrad.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *   http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use reqwest::header::HeaderMap;
18
19use crate::error::{Error, Result};
20
21/// Issues a `GET` to `url` (with optional `headers`) and returns the full response body.
22///
23/// If `show_progress` is set, renders an `indicatif` progress bar (labeled `name`) as the body
24/// streams in; otherwise the body is read in one shot.
25pub async fn fetch_content(
26    name: &str,
27    url: &str,
28    headers: Option<HeaderMap>,
29    show_progress: bool,
30) -> Result<Vec<u8>> {
31    let client = reqwest::Client::new();
32    let mut request = client.get(url);
33    if let Some(headers) = headers {
34        request = request.headers(headers);
35    }
36
37    let mut response = request.send().await.map_err(Error::RequestFailed)?;
38    let total_size = response.content_length().unwrap_or(0);
39    let mut content = Vec::new();
40
41    if !response.status().is_success() {
42        return Err(Error::HttpError(response.status()).into());
43    }
44
45    if show_progress {
46        let pb = indicatif::ProgressBar::new(total_size);
47        pb.set_style(indicatif::ProgressStyle::default_bar()
48          .template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta})")
49          .unwrap()
50          .progress_chars("#>-"));
51        pb.set_message(name.to_string());
52
53        while let Some(chunk) = response.chunk().await.map_err(Error::RequestFailed)? {
54            content.extend_from_slice(&chunk);
55            pb.inc(chunk.len() as u64);
56        }
57        pb.finish_with_message("Download complete");
58    } else {
59        content = response
60            .bytes()
61            .await
62            .map_err(Error::RequestFailed)?
63            .to_vec();
64    }
65
66    Ok(content)
67}