dprint_cli_core/
url_utils.rs

1use anyhow::bail;
2use anyhow::Result;
3use std::io::Read;
4
5use crate::logging::ProgressBarStyle;
6use crate::logging::ProgressBars;
7
8pub fn download_url(url: &str, progress_bars: &Option<ProgressBars>, read_env_var: impl Fn(&str) -> Option<String>) -> Result<Option<Vec<u8>>> {
9  let resp = match build_agent(url, read_env_var)?.get(url).call() {
10    Ok(resp) => resp,
11    Err(ureq::Error::Status(404, _)) => {
12      return Ok(None);
13    }
14    Err(err) => {
15      bail!("Error downloading {} - Error: {:#}", url, err)
16    }
17  };
18
19  let total_size = resp.header("Content-Length").and_then(|s| s.parse::<usize>().ok()).unwrap_or(0);
20  let mut reader = resp.into_reader();
21  match inner_download(url, &mut reader, total_size, progress_bars) {
22    Ok(result) => Ok(Some(result)),
23    Err(err) => bail!("Error downloading {} - {:#}", url, err),
24  }
25}
26
27fn inner_download(url: &str, reader: &mut impl Read, total_size: usize, progress_bars: &Option<ProgressBars>) -> Result<Vec<u8>> {
28  let mut final_bytes = Vec::with_capacity(total_size);
29  if let Some(progress_bars) = &progress_bars {
30    let mut buf: [u8; 512] = [0; 512]; // ensure progress bars update often
31    let message = format!("Downloading {}", url);
32    let pb = progress_bars.add_progress(message, ProgressBarStyle::Download, total_size);
33    loop {
34      let bytes_read = reader.read(&mut buf)?;
35      if bytes_read == 0 {
36        break;
37      }
38      final_bytes.extend(&buf[..bytes_read]);
39      pb.set_position(final_bytes.len());
40    }
41    pb.finish();
42  } else {
43    reader.read_to_end(&mut final_bytes)?;
44  }
45  Ok(final_bytes)
46}
47
48fn build_agent(url: &str, read_env_var: impl Fn(&str) -> Option<String>) -> Result<ureq::Agent> {
49  let mut agent = ureq::AgentBuilder::new();
50  if let Some(proxy_url) = get_proxy_url(url, read_env_var) {
51    agent = agent.proxy(ureq::Proxy::new(proxy_url)?);
52  }
53  Ok(agent.build())
54}
55
56fn get_proxy_url(url: &str, read_env_var: impl Fn(&str) -> Option<String>) -> Option<String> {
57  let lower_url = url.to_lowercase();
58  if lower_url.starts_with("https://") {
59    read_proxy_env_var("HTTPS_PROXY", read_env_var)
60  } else if lower_url.starts_with("http://") {
61    read_proxy_env_var("HTTP_PROXY", read_env_var)
62  } else {
63    None
64  }
65}
66
67fn read_proxy_env_var(env_var_name: &str, read_env_var: impl Fn(&str) -> Option<String>) -> Option<String> {
68  read_env_var(&env_var_name.to_uppercase()).or_else(|| read_env_var(&env_var_name.to_lowercase()))
69}