diff --git a/src/downloader/downloader.rs b/src/downloader/downloader.rs index 59db16a..5c61121 100644 --- a/src/downloader/downloader.rs +++ b/src/downloader/downloader.rs @@ -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 { /// The webclient client: Arc, /// The download status - status: Arc, + pd: Arc, /// The url of the file url: Arc, /// The HTTP headers map headers: Arc>, /// The target file - file: RwLock>, + file: RwLock>, + /// The status of the downloader + status: RwLock, + /// All tasks + tasks: Vec>, + /// Whether to enable mulitple thread mode + multi: AtomicBool, } -impl Downloader { +impl Downloader { /// 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 + ?Sized>(url: U, headers: H, path: Option<&P>, overwrite: Option) -> Result { + pub fn new + ?Sized>(url: U, headers: H, path: Option<&P>, overwrite: Option) -> Result, 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 Downloader { + /// 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() + } + } +} diff --git a/src/downloader/enums.rs b/src/downloader/enums.rs index 6ace131..d558c0e 100644 --- a/src/downloader/enums.rs +++ b/src/downloader/enums.rs @@ -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 { /// Created successfully - Ok(Downloader), + Ok(Downloader), /// 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, +} diff --git a/src/downloader/pd_file/mod.rs b/src/downloader/pd_file/mod.rs index 75f4e78..8ac1b07 100644 --- a/src/downloader/pd_file/mod.rs +++ b/src/downloader/pd_file/mod.rs @@ -12,3 +12,4 @@ pub mod version; pub use enums::PdFileResult; pub use error::PdFileError; pub use file::PdFile; +pub use part_status::PdFilePartStatus; diff --git a/src/ext/atomic.rs b/src/ext/atomic.rs new file mode 100644 index 0000000..142d114 --- /dev/null +++ b/src/ext/atomic.rs @@ -0,0 +1,40 @@ +use std::sync::atomic::Ordering; + +/// A trait to help to load and store atomic value quickly. +pub trait AtomicQuick { + /// 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); diff --git a/src/ext/mod.rs b/src/ext/mod.rs index 8c87a1b..7588f22 100644 --- a/src/ext/mod.rs +++ b/src/ext/mod.rs @@ -1,3 +1,4 @@ +pub mod atomic; pub mod cstr; #[cfg(feature = "flagset")] pub mod flagset;