diff --git a/src/downloader/downloader.rs b/src/downloader/downloader.rs index dddc32a..6785638 100644 --- a/src/downloader/downloader.rs +++ b/src/downloader/downloader.rs @@ -3,18 +3,21 @@ use super::pd_file::PdFileResult; use super::enums::DownloaderResult; use super::enums::DownloaderStatus; use super::error::DownloaderError; +use super::local_file::LocalFile; use super::tasks::create_download_tasks_simple; use crate::ext::atomic::AtomicQuick; +use crate::ext::io::ClearFile; use crate::ext::rw_lock::GetRwLock; +use crate::ext::try_err::TryErr; 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::ops::DerefMut; use std::path::Path; use std::sync::Arc; use std::sync::RwLock; @@ -24,7 +27,7 @@ use url::Url; #[derive(Debug)] /// A file downloader -pub struct DownloaderInternal { +pub struct DownloaderInternal { /// The webclient pub client: Arc, /// The download status @@ -43,7 +46,7 @@ pub struct DownloaderInternal { multi: AtomicBool, } -impl DownloaderInternal { +impl DownloaderInternal { /// Create a new [DownloaderInternal] instance /// * `url` - The url of the file /// * `header` - HTTP headers @@ -91,9 +94,9 @@ impl DownloaderInternal { let file = match path { Some(p) => { if already_exists { - Some(File::open(p)?) + Some(LocalFile::open(p)?) } else { - Some(File::create(p)?) + Some(LocalFile::create(p)?) } } None => { None } @@ -111,13 +114,22 @@ impl DownloaderInternal { } } -impl DownloaderInternal { +impl DownloaderInternal { /// Add a new task to tasks /// * `task` - Task pub fn add_task(&self, task: JoinHandle>) { self.tasks.get_mut().push(task) } + /// Clear all datas in file + pub fn clear_file(&self) -> std::io::Result<()> { + match self.file.get_mut().deref_mut() { + Some(f) => { f.clear_file()? } + None => {} + }; + Ok(()) + } + /// Return the status of the downloader. pub fn get_status(&self) -> DownloaderStatus { self.status.get_ref().clone() @@ -138,22 +150,32 @@ impl DownloaderInternal { self.multi.qload() } } + + /// Write datas to the file. + /// * `data` - Data + pub fn write(&self, data: &[u8]) -> Result<(), DownloaderError> { + match self.file.get_mut().deref_mut() { + Some(f) => { f.write_all(data)? } + None => {} + } + Ok(()) + } } /// A file downloader -pub struct Downloader { +pub struct Downloader { /// internal type downloader: Arc>, } -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, DownloaderError> { - Ok(match DownloaderInternal::::new(url, headers, path, overwrite)? { + Ok(match DownloaderInternal::::new(url, headers, path, overwrite)? { DownloaderResult::Ok(d) => { DownloaderResult::Ok(Self { downloader: Arc::new(d) }) } @@ -173,7 +195,7 @@ macro_rules! define_downloader_fn { } } -impl Downloader { +impl Downloader { /// Start download if download not started. /// /// Returns the status of the Downloader diff --git a/src/downloader/local_file.rs b/src/downloader/local_file.rs new file mode 100644 index 0000000..8d632a4 --- /dev/null +++ b/src/downloader/local_file.rs @@ -0,0 +1,73 @@ +use crate::ext::io::ClearFile; +use std::fs::File; +use std::fs::remove_file; +use std::io::Seek; +use std::io::Write; +use std::ops::Deref; +use std::path::Path; +use std::path::PathBuf; + +pub struct LocalFile { + file: Option, + path: PathBuf, +} + +impl LocalFile { + /// Opens a file in write-only mode. + /// + /// This function will create a file if it does not exist, and will truncate it if it does. + pub fn create>(path: P) -> std::io::Result { + let p = path.as_ref().to_owned(); + let f = File::create(&p)?; + Ok(Self { + file: Some(f), + path: p, + }) + } + + /// Attempts to open a file in read-only mode. + pub fn open>(path: P) -> std::io::Result { + let p = path.as_ref().to_owned(); + let f = File::open(&p)?; + Ok(Self { + file: Some(f), + path: p, + }) + } +} + +impl ClearFile for LocalFile { + fn clear_file(&mut self) -> std::io::Result<()> { + self.file.take(); + remove_file(&self.path)?; + self.file.replace(File::create(&self.path)?); + Ok(()) + } +} + +impl Deref for LocalFile { + type Target = File; + fn deref(&self) -> &Self::Target { + &self.file.as_ref().unwrap() + } +} + +impl Seek for LocalFile { + fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result { + self.file.as_ref().unwrap().seek(pos) + } +} + +impl Write for LocalFile { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.file.as_ref().unwrap().write(buf) + } + + fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result { + self.file.as_ref().unwrap().write_vectored(bufs) + } + + fn flush(&mut self) -> std::io::Result<()> { + self.file.as_ref().unwrap().flush() + } +} diff --git a/src/downloader/mod.rs b/src/downloader/mod.rs index 8b89889..61f1cbf 100644 --- a/src/downloader/mod.rs +++ b/src/downloader/mod.rs @@ -4,8 +4,11 @@ pub mod downloader; pub mod enums; /// File downloader's error pub mod error; +/// Local file type +pub mod local_file; /// The pd file pub mod pd_file; /// Deal download tasks pub mod tasks; pub use downloader::Downloader; +pub use error::DownloaderError; diff --git a/src/downloader/tasks.rs b/src/downloader/tasks.rs index 1723392..b3348fd 100644 --- a/src/downloader/tasks.rs +++ b/src/downloader/tasks.rs @@ -1,3 +1,4 @@ +use crate::ext::io::ClearFile; use crate::ext::try_err::TryErr; use crate::gettext; use super::error::DownloaderError; @@ -9,7 +10,7 @@ use std::io::Write; use std::sync::Arc; /// Create a download tasks in simple thread mode. -pub async fn create_download_tasks_simple(d: Arc>) -> Result<(), DownloaderError> { +pub async fn create_download_tasks_simple(d: Arc>) -> Result<(), DownloaderError> { let start = if d.pd.is_downloading() { d.pd.get_downloaded_file_size() } else { @@ -55,6 +56,7 @@ pub async fn create_download_tasks_simple(d: Arc< }; if need_reget { d.pd.clear()?; + d.clear_file()?; result = d.client.get(d.url.deref().clone(), d.headers.deref().clone()).try_err(gettext("Failed to get url."))?; status = result.status(); } diff --git a/src/ext/io.rs b/src/ext/io.rs index d925a34..02bb0a5 100644 --- a/src/ext/io.rs +++ b/src/ext/io.rs @@ -51,3 +51,9 @@ impl StructRead for T { } } } + +/// Clear all datas in file +pub trait ClearFile { + /// Clear all datas in file + fn clear_file(&mut self) -> std::io::Result<()>; +}