fast_down/utils/
file.rs

1use crate::file;
2use crate::writer::file::SeqFileWriter;
3use crate::{auto, DownloadResult, ProgressEntry};
4use reqwest::{Client, IntoUrl};
5use std::{io::ErrorKind, path::Path, time::Duration};
6use tokio::fs::{self, OpenOptions};
7
8#[derive(Debug, Clone)]
9pub struct DownloadOptions {
10    pub threads: usize,
11    pub client: Client,
12    pub can_fast_download: bool,
13    pub write_buffer_size: usize,
14    pub download_chunks: Vec<ProgressEntry>,
15    pub retry_gap: Duration,
16    pub file_size: u64,
17}
18
19#[derive(Debug)]
20pub enum DownloadErrorKind {
21    Reqwest(reqwest::Error),
22    Io(std::io::Error),
23}
24impl std::fmt::Display for DownloadErrorKind {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        match self {
27            DownloadErrorKind::Reqwest(err) => write!(f, "HTTP request failed: {}", err),
28            DownloadErrorKind::Io(err) => write!(f, "IO error: {}", err),
29        }
30    }
31}
32
33impl std::error::Error for DownloadErrorKind {
34    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
35        match self {
36            DownloadErrorKind::Reqwest(err) => Some(err),
37            DownloadErrorKind::Io(err) => Some(err),
38        }
39    }
40}
41
42pub async fn download(
43    url: impl IntoUrl,
44    save_path: &Path,
45    options: DownloadOptions,
46) -> Result<DownloadResult, DownloadErrorKind> {
47    let save_folder = save_path.parent().unwrap();
48    if let Err(e) = fs::create_dir_all(save_folder).await {
49        if e.kind() != ErrorKind::AlreadyExists {
50            return Err(DownloadErrorKind::Io(e));
51        }
52    }
53    let file = OpenOptions::new()
54        .read(true)
55        .write(true)
56        .create(true)
57        .open(&save_path)
58        .await
59        .map_err(|e| DownloadErrorKind::Io(e))?;
60    let seq_file_writer = SeqFileWriter::new(
61        file.try_clone()
62            .await
63            .map_err(|e| DownloadErrorKind::Io(e))?,
64        options.write_buffer_size,
65    );
66    #[cfg(target_pointer_width = "64")]
67    let rand_file_writer = file::rand_file_writer_mmap::RandFileWriter::new(
68        file,
69        options.file_size,
70        options.write_buffer_size,
71    )
72    .await
73    .map_err(|e| DownloadErrorKind::Io(e))?;
74    #[cfg(not(target_pointer_width = "64"))]
75    let rand_file_writer = file::rand_file_writer_std::RandFileWriter::new(
76        file,
77        options.file_size,
78        options.write_buffer_size,
79    )
80    .await
81    .map_err(|e| DownloadErrorKind::Io(e))?;
82    auto::download(
83        url,
84        seq_file_writer,
85        rand_file_writer,
86        auto::DownloadOptions {
87            threads: options.threads,
88            client: options.client,
89            can_fast_download: options.can_fast_download,
90            download_chunks: options.download_chunks,
91            retry_gap: options.retry_gap,
92            file_size: options.file_size,
93        },
94    )
95    .await
96    .map_err(|e| DownloadErrorKind::Reqwest(e))
97}