This commit is contained in:
2022-05-29 12:38:19 +00:00
committed by GitHub
parent ad94ec1a96
commit 3f2a9d6ff6
5 changed files with 111 additions and 8 deletions

View File

@@ -1,40 +1,55 @@
use super::pd_file::PdFile;
use super::pd_file::PdFilePartStatus;
use super::pd_file::PdFileResult;
use super::enums::DownloaderResult;
use super::enums::DownloaderStatus;
use super::error::DownloaderError;
use crate::ext::atomic::AtomicQuick;
use crate::ext::rw_lock::GetRwLock;
use crate::utils::ask_need_overwrite;
use crate::webclient::WebClient;
use crate::webclient::ToHeaders;
use reqwest::IntoUrl;
use std::collections::HashMap;
use std::fs::File;
use std::fs::remove_file;
use std::io::Seek;
use std::io::Write;
use std::path::Path;
use std::sync::Arc;
use std::sync::RwLock;
use std::sync::atomic::AtomicBool;
use tokio::task::JoinHandle;
use url::Url;
#[derive(Debug)]
/// A file downloader
pub struct Downloader {
pub struct Downloader<T: Write + Seek> {
/// The webclient
client: Arc<WebClient>,
/// The download status
status: Arc<PdFile>,
pd: Arc<PdFile>,
/// The url of the file
url: Arc<Url>,
/// The HTTP headers map
headers: Arc<HashMap<String, String>>,
/// The target file
file: RwLock<Option<File>>,
file: RwLock<Option<T>>,
/// The status of the downloader
status: RwLock<DownloaderStatus>,
/// All tasks
tasks: Vec<JoinHandle<PdFilePartStatus>>,
/// Whether to enable mulitple thread mode
multi: AtomicBool,
}
impl Downloader {
impl Downloader<File> {
/// Create a new [Downloader] instance
/// * `url` - The url of the file
/// * `header` - HTTP headers
/// * `path` - The path to store downloaded file.
/// * `overwrite` - Whether to overwrite file
pub fn new<U: IntoUrl, H: ToHeaders, P: AsRef<Path> + ?Sized>(url: U, headers: H, path: Option<&P>, overwrite: Option<bool>) -> Result<DownloaderResult, DownloaderError> {
pub fn new<U: IntoUrl, H: ToHeaders, P: AsRef<Path> + ?Sized>(url: U, headers: H, path: Option<&P>, overwrite: Option<bool>) -> Result<DownloaderResult<File>, DownloaderError> {
let h = match headers.to_headers() {
Some(h) => { h }
None => { HashMap::new() }
@@ -50,6 +65,7 @@ impl Downloader {
if !overwrite {
return Ok(DownloaderResult::Canceled);
} else {
remove_file(p)?;
PdFile::new()
}
}
@@ -57,6 +73,7 @@ impl Downloader {
if !ask_need_overwrite(p.to_str().unwrap()) {
return Ok(DownloaderResult::Canceled);
} else {
remove_file(p)?;
PdFile::new()
}
}
@@ -83,10 +100,41 @@ impl Downloader {
};
Ok(DownloaderResult::Ok(Self {
client: Arc::new(WebClient::new()),
status: Arc::new(pd_file),
pd: Arc::new(pd_file),
url: Arc::new(url.into_url()?),
headers: Arc::new(h),
file: RwLock::new(file),
status: RwLock::new(DownloaderStatus::Created),
tasks: Vec::new(),
multi: AtomicBool::new(false),
}))
}
}
impl <T: Write + Seek> Downloader<T> {
/// Start download if download not started.
///
/// Returns the status of the Downloader
pub fn download(&self) -> DownloaderStatus {
if !self.is_created() {
return self.status.get_ref().clone();
}
self.status.get_ref().clone()
}
#[inline]
/// Returns true if the downloader is created just now.
pub fn is_created(&self) -> bool {
*self.status.get_ref() == DownloaderStatus::Created
}
#[inline]
/// Returns true if is multiple thread mode.
pub fn is_multi_threads(&self) -> bool {
if self.pd.is_downloading() {
self.pd.is_multi_threads()
} else {
self.multi.qload()
}
}
}

View File

@@ -1,10 +1,23 @@
use super::downloader::Downloader;
use std::io::Seek;
use std::io::Write;
#[derive(Debug)]
/// The result when try create a new [Downloader] interface
pub enum DownloaderResult {
pub enum DownloaderResult<T: Write + Seek> {
/// Created successfully
Ok(Downloader),
Ok(Downloader<T>),
/// The target file already downloaded and overwrite is disabled.
Canceled,
}
#[derive(Clone, Debug, PartialEq, PartialOrd)]
/// The status of the [Downloader]
pub enum DownloaderStatus {
/// The downloader is just created
Created,
/// The downloader is downloading now
Downloading,
/// The downloader is downloaded complete
Downloaded,
}

View File

@@ -12,3 +12,4 @@ pub mod version;
pub use enums::PdFileResult;
pub use error::PdFileError;
pub use file::PdFile;
pub use part_status::PdFilePartStatus;

40
src/ext/atomic.rs Normal file
View File

@@ -0,0 +1,40 @@
use std::sync::atomic::Ordering;
/// A trait to help to load and store atomic value quickly.
pub trait AtomicQuick<T> {
/// Loads a value from the atomic integer
fn qload(&self) -> T;
/// Stores a value into the atomic integer.
fn qstore(&self, value: T);
#[inline]
/// Stores a value into the atomic integer.
/// Alias for [Self::qstore]
fn qsave(&self, value: T) {
self.qstore(value)
}
}
macro_rules! impl_atomic_quick_with_atomic {
($type1:ty, $type2:ty) => {
impl AtomicQuick<$type2> for $type1 {
fn qload(&self) -> $type2 {
self.load(Ordering::Relaxed)
}
fn qstore(&self, value: $type2) {
self.store(value, Ordering::Relaxed)
}
}
}
}
impl_atomic_quick_with_atomic!(std::sync::atomic::AtomicBool, bool);
impl_atomic_quick_with_atomic!(std::sync::atomic::AtomicI8, i8);
impl_atomic_quick_with_atomic!(std::sync::atomic::AtomicU8, u8);
impl_atomic_quick_with_atomic!(std::sync::atomic::AtomicI16, i16);
impl_atomic_quick_with_atomic!(std::sync::atomic::AtomicU16, u16);
impl_atomic_quick_with_atomic!(std::sync::atomic::AtomicI32, i32);
impl_atomic_quick_with_atomic!(std::sync::atomic::AtomicU32, u32);
impl_atomic_quick_with_atomic!(std::sync::atomic::AtomicI64, i64);
impl_atomic_quick_with_atomic!(std::sync::atomic::AtomicU64, u64);
impl_atomic_quick_with_atomic!(std::sync::atomic::AtomicIsize, isize);
impl_atomic_quick_with_atomic!(std::sync::atomic::AtomicUsize, usize);

View File

@@ -1,3 +1,4 @@
pub mod atomic;
pub mod cstr;
#[cfg(feature = "flagset")]
pub mod flagset;