Skip to main content

dora_download/
lib.rs

1use eyre::{Context, ContextCompat};
2#[cfg(unix)]
3use std::os::unix::prelude::PermissionsExt;
4use std::path::{Path, PathBuf};
5use tokio::io::AsyncWriteExt;
6
7fn get_filename(response: &reqwest::Response) -> Option<String> {
8    if let Some(content_disposition) = response.headers().get("content-disposition") {
9        if let Ok(filename) = content_disposition.to_str() {
10            if let Some(name) = filename.split("filename=").nth(1) {
11                return Some(name.trim_matches('"').to_string());
12            }
13        }
14    }
15
16    // If Content-Disposition header is not available, extract from URL
17    let path = Path::new(response.url().as_str());
18    if let Some(name) = path.file_name() {
19        if let Some(filename) = name.to_str() {
20            return Some(filename.to_string());
21        }
22    }
23
24    None
25}
26
27pub async fn download_file<T>(url: T, target_dir: &Path) -> Result<PathBuf, eyre::ErrReport>
28where
29    T: reqwest::IntoUrl + std::fmt::Display + Copy,
30{
31    tokio::fs::create_dir_all(&target_dir)
32        .await
33        .wrap_err("failed to create parent folder")?;
34
35    let response = reqwest::get(url)
36        .await
37        .wrap_err_with(|| format!("failed to request operator from `{url}`"))?;
38
39    let filename = get_filename(&response).context("Could not find a filename")?;
40    let bytes = response
41        .bytes()
42        .await
43        .wrap_err("failed to read operator from `{uri}`")?;
44    let path = target_dir.join(filename);
45    let mut file = tokio::fs::File::create(&path)
46        .await
47        .wrap_err("failed to create target file")?;
48    file.write_all(&bytes)
49        .await
50        .wrap_err("failed to write downloaded operator to file")?;
51    file.sync_all().await.wrap_err("failed to `sync_all`")?;
52
53    #[cfg(unix)]
54    file.set_permissions(std::fs::Permissions::from_mode(0o764))
55        .await
56        .wrap_err("failed to make downloaded file executable")?;
57
58    Ok(path.to_path_buf())
59}